Merge tag 'v22.3.0' into develop
rels
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
from rowers.models import Rower, PaidPlan, CoachingGroup
|
from rowers.models import Rower, PaidPlan, CoachingGroup, iDokladToken
|
||||||
from rowers.utils import ProcessorCustomerError
|
from rowers.utils import ProcessorCustomerError
|
||||||
from rowsandall_app.settings import (
|
from rowsandall_app.settings import (
|
||||||
BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY,
|
BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY,
|
||||||
BRAINTREE_SANDBOX_MERCHANT_ID, BRAINTREE_SANDBOX_PUBLIC_KEY,
|
BRAINTREE_SANDBOX_MERCHANT_ID, BRAINTREE_SANDBOX_PUBLIC_KEY,
|
||||||
BRAINTREE_SANDBOX_PRIVATE_KEY, BRAINTREE_MERCHANT_ACCOUNT_ID
|
BRAINTREE_SANDBOX_PRIVATE_KEY, BRAINTREE_MERCHANT_ACCOUNT_ID,
|
||||||
|
IDOKLAD_CLIENT_ID, IDOKLAD_CLIENT_SECRET, IDOKLAD_REDIRECT_URI,
|
||||||
)
|
)
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from rowers.utils import dologging
|
from rowers.utils import dologging
|
||||||
@@ -16,7 +17,7 @@ from rowers.tasks import (
|
|||||||
# handle_send_email_transaction_notification,
|
# handle_send_email_transaction_notification,
|
||||||
)
|
)
|
||||||
from rowers.utils import myqueue
|
from rowers.utils import myqueue
|
||||||
import rowers.fakturoid as fakturoid
|
import rowers.idoklad as idoklad
|
||||||
from braintree.exceptions.invalid_signature_error import InvalidSignatureError
|
from braintree.exceptions.invalid_signature_error import InvalidSignatureError
|
||||||
from braintree.exceptions.not_found_error import NotFoundError
|
from braintree.exceptions.not_found_error import NotFoundError
|
||||||
import time
|
import time
|
||||||
@@ -52,6 +53,7 @@ else:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_webhook(notification):
|
def process_webhook(notification):
|
||||||
if not settings.TESTING: # pragma: no cover
|
if not settings.TESTING: # pragma: no cover
|
||||||
@@ -112,18 +114,18 @@ def send_invoice(subscription):
|
|||||||
else:
|
else:
|
||||||
r = rs[0]
|
r = rs[0]
|
||||||
dologging('braintreewebhooks.log','Rower '+str(r)+'\n')
|
dologging('braintreewebhooks.log','Rower '+str(r)+'\n')
|
||||||
fakturoid_contact_id = fakturoid.get_contacts(r)
|
idoklad_contact_id = idoklad.get_contacts(r)
|
||||||
dologging('braintreewebhooks.log','Fakturoid Contact ID '+str(fakturoid_contact_id)+'\n')
|
dologging('braintreewebhooks.log','Idoklad Contact ID '+str(idoklad_contact_id)+'\n')
|
||||||
if not fakturoid_contact_id: # pragma: no cover
|
if not idoklad_contact_id: # pragma: no cover
|
||||||
fakturoid_contact_id = fakturoid.create_contact(r)
|
idoklad_contact_id = idoklad.create_contact(r)
|
||||||
dologging('braintreewebhooks.log','Created Fakturoid Contact ID ' +
|
dologging('braintreewebhooks.log','Created Idoklad Contact ID ' +
|
||||||
str(fakturoid_contact_id)+'\n')
|
str(idoklad_contact_id)+'\n')
|
||||||
transactions = subscription.transactions
|
transactions = subscription.transactions
|
||||||
if transactions:
|
if transactions:
|
||||||
amount = transactions[0].amount
|
amount = transactions[0].amount
|
||||||
dologging('braintreewebhooks.log','Transaction amount '+str(amount)+'\n')
|
dologging('braintreewebhooks.log','Transaction amount '+str(amount)+'\n')
|
||||||
id = fakturoid.create_invoice(r, amount, subscription_id, dosend=True,
|
id = idoklad.create_invoice(r, amount, subscription_id, dosend=True,
|
||||||
contact_id=fakturoid_contact_id)
|
contact_id=idoklad_contact_id)
|
||||||
return id
|
return id
|
||||||
|
|
||||||
return 0 # pragma: no cover
|
return 0 # pragma: no cover
|
||||||
@@ -210,11 +212,13 @@ def make_payment(rower, data):
|
|||||||
l=rower.user.last_name,
|
l=rower.user.last_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
fakturoid_contact_id = fakturoid.get_contacts(rower)
|
idoklad_contact_id = idoklad.get_contacts(rower)
|
||||||
if not fakturoid_contact_id:
|
if not idoklad_contact_id:
|
||||||
fakturoid_contact_id = fakturoid.create_contact(rower)
|
idoklad_contact_id = idoklad.create_contact(rower)
|
||||||
_ = fakturoid.create_invoice(rower, amount, transaction.id, dosend=True, contact_id=fakturoid_contact_id,
|
|
||||||
name=additional_text)
|
_ = idoklad.create_invoice(rower, amount, transaction.id, dosend=True,
|
||||||
|
contact_id=idoklad_contact_id,
|
||||||
|
name=additional_text)
|
||||||
|
|
||||||
_ = myqueue(queuehigh, handle_send_email_transaction,
|
_ = myqueue(queuehigh, handle_send_email_transaction,
|
||||||
name, rower.user.email, amount)
|
name, rower.user.email, amount)
|
||||||
|
|||||||
204
rowers/idoklad.py
Normal file
204
rowers/idoklad.py
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import requests
|
||||||
|
import json, yaml
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
import urllib.parse
|
||||||
|
from rowers.utils import dologging
|
||||||
|
import datetime
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from rowsandall_app.settings import (
|
||||||
|
IDOKLAD_CLIENT_ID, IDOKLAD_CLIENT_SECRET,
|
||||||
|
)
|
||||||
|
|
||||||
|
contacts_url = 'https://api.idoklad.cz/v3/Contacts'
|
||||||
|
invoice_url = 'https://api.idoklad.cz/v3/IssuedInvoices'
|
||||||
|
email_url = 'https://api.idoklad.cz/v3/Mails/IssuedInvoice/Send'
|
||||||
|
|
||||||
|
from rowers.models import iDokladToken
|
||||||
|
|
||||||
|
#idoklad_countries = json.loads(open('rowers/idoklad_countries.json').read())["Data"]["Items"]
|
||||||
|
with open('rowers/idoklad_countries.yaml') as f:
|
||||||
|
idoklad_countries = yaml.load(f, Loader=yaml.FullLoader)["Data"]["Items"]
|
||||||
|
|
||||||
|
def get_country_id(code):
|
||||||
|
for c in idoklad_countries:
|
||||||
|
if c['Code'] == code:
|
||||||
|
return c['Id']
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def idoklad_token():
|
||||||
|
try:
|
||||||
|
token = iDokladToken.objects.get(id=1)
|
||||||
|
except iDokladToken.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if token.updated_at + datetime.timedelta(seconds=token.expires_in) < timezone.now():
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'grant_type': 'refresh_token',
|
||||||
|
'client_id': IDOKLAD_CLIENT_ID,
|
||||||
|
'client_secret': IDOKLAD_CLIENT_SECRET,
|
||||||
|
'scope': 'eet offline_access',
|
||||||
|
'refresh_token': token.refresh_token,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post('https://identity.idoklad.cz/server/connect/token', headers=headers, data=data)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
token = response.json()
|
||||||
|
token['updated_at'] = timezone.now()
|
||||||
|
token = iDokladToken.objects.filter(id=1).update(**token)
|
||||||
|
return iDokladToken.objects.get(id=1)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return token
|
||||||
|
|
||||||
|
def get_contacts(rower):
|
||||||
|
token = idoklad_token()
|
||||||
|
if token is None:
|
||||||
|
return None
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer {t}'.format(t=token.access_token),
|
||||||
|
}
|
||||||
|
|
||||||
|
url = contacts_url+'?filter=(Email~eq~'+urllib.parse.quote(rower.user.email)+')'
|
||||||
|
dologging('idoklad.log','Searching Contact url: '+str(url))
|
||||||
|
|
||||||
|
res = requests.get(url, headers=headers)
|
||||||
|
|
||||||
|
dologging('idoklad.log','Searching Contact Status code '+str(res.status_code)+'\n')
|
||||||
|
|
||||||
|
if res.status_code != 200: # pragma: no cover
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = res.json()['Data']['Items']
|
||||||
|
|
||||||
|
if len(data) >= 1:
|
||||||
|
r = data[0]
|
||||||
|
return r['Id']
|
||||||
|
|
||||||
|
return None # pragma
|
||||||
|
|
||||||
|
def create_contact(rower):
|
||||||
|
token = idoklad_token()
|
||||||
|
if token is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'City': rower.city,
|
||||||
|
'CompanyName': rower.user.first_name+" "+rower.user.last_name,
|
||||||
|
'CountryId': get_country_id(rower.country.code),
|
||||||
|
'Email': rower.user.email,
|
||||||
|
'Firstname': rower.user.first_name,
|
||||||
|
'PostalCode': rower.postal_code,
|
||||||
|
'Street': rower.street_address,
|
||||||
|
'Surname': rower.user.last_name,
|
||||||
|
'DeliveryAddresses': []
|
||||||
|
}
|
||||||
|
|
||||||
|
if rower.country.numeric is None:
|
||||||
|
data['CountryId'] = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dologging('idoklad.log','Creating idoklad contact for '+str(rower.user.email)+'\n')
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer {t}'.format(t=token.access_token),
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
res = requests.post(contacts_url, json=data, headers=headers)
|
||||||
|
|
||||||
|
if res.status_code not in [200, 201]:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
id = res.json()['Data']['Id']
|
||||||
|
|
||||||
|
return id
|
||||||
|
|
||||||
|
def create_invoice(rower, amount, braintreeid, dosend=True, contact_id=None, name=None):
|
||||||
|
t = idoklad_token()
|
||||||
|
if t is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not contact_id:
|
||||||
|
contact_id = get_contacts(rower)
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
name = 'Rowsandall Subscription '+str(braintreeid)
|
||||||
|
|
||||||
|
if not contact_id:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
token = idoklad_token()
|
||||||
|
if token is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
dologging('idoklad.log','Creating idoklad invoice for '+str(rower.user.email)+'\n')
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer {t}'.format(t=token.access_token),
|
||||||
|
}
|
||||||
|
|
||||||
|
res = requests.get(invoice_url+'/Default', headers=headers)
|
||||||
|
|
||||||
|
post_data = res.json()['Data']
|
||||||
|
post_data['DateOfPayment'] = timezone.now().strftime('%Y-%m-%d')
|
||||||
|
post_data['Description'] = name
|
||||||
|
post_data['Items'][0]['Name'] = name
|
||||||
|
post_data['Items'][0]['UnitPrice'] = amount
|
||||||
|
post_data['PartnerId'] = contact_id
|
||||||
|
post_data['ItemsTextPrefix'] = 'We invoice you for '+str(name)+' in the amount of '+str(amount)+' EUR.'
|
||||||
|
post_data['ItemsTextSuffix'] = 'This invoice was already paid. Please do not pay it again.'
|
||||||
|
post_data['Items'][0]['VatRate'] = 0.0
|
||||||
|
post_data['Items'][0]['VatRateType'] = 2
|
||||||
|
post_data['Items'][0]['VatCodeId'] = 3
|
||||||
|
post_data['CurrencyId'] = 2
|
||||||
|
post_data['ReportLanguage'] = 3
|
||||||
|
post_data.pop('ExchangeRate', None)
|
||||||
|
|
||||||
|
|
||||||
|
res = requests.post(invoice_url, json=post_data, headers=headers)
|
||||||
|
dologging('idoklad.log','Invoice Created - status code '+str(res.status_code)+'\n')
|
||||||
|
|
||||||
|
if res.status_code not in [200, 201]:
|
||||||
|
dologging('idoklad.log','Invoice Created - reason '+str(res.reason)+'\n')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
id = res.json()['Data']['Id']
|
||||||
|
|
||||||
|
if dosend:
|
||||||
|
data = {
|
||||||
|
'AttachmentIds': [],
|
||||||
|
'DocumentId': id,
|
||||||
|
'EmailBody': 'Dear customer, we are sending you the invoice for your subscription. Please do not hesitate to contact us if you have any questions. Best regards, Rowsandall Team',
|
||||||
|
'EmailSubject': 'Rowsandall Subscription Invoice',
|
||||||
|
'Method': 1,
|
||||||
|
'ReportLanguage': 3,
|
||||||
|
'SendToSelf': True,
|
||||||
|
'SendToPartner': True,
|
||||||
|
'SendToAccountant': False,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer {access_token}'.format(access_token=token.access_token),
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
|
||||||
|
res = requests.post(email_url, json=data, headers=headers)
|
||||||
|
|
||||||
|
dologging('idoklad.log','Invoice Sent - status code '+str(res.status_code)+'\n')
|
||||||
|
if res.status_code not in [200, 201]:
|
||||||
|
dologging('idoklad.log','Invoice Sent - reason '+str(res.text)+'\n')
|
||||||
|
|
||||||
|
return id
|
||||||
|
|
||||||
2212
rowers/idoklad_countries.json
Normal file
2212
rowers/idoklad_countries.json
Normal file
File diff suppressed because it is too large
Load Diff
1808
rowers/idoklad_countries.yaml
Normal file
1808
rowers/idoklad_countries.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5376,3 +5376,16 @@ class ForceCurveAnalysis(models.Model):
|
|||||||
date = self.date)
|
date = self.date)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
class iDokladToken(models.Model):
|
||||||
|
access_token = models.CharField(max_length=512)
|
||||||
|
refresh_token = models.CharField(max_length=512)
|
||||||
|
id_token = models.CharField(max_length=512)
|
||||||
|
token_type = models.CharField(max_length=512)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
expires_in = models.IntegerField() # Store token expiry duration in seconds
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"iDoklad Token updated at {self.updated_at}, expires at {self.updated_at+datetime.timedelta(seconds=self.expires_in)}"
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ from rowers.dataprep import delete_strokedata
|
|||||||
from redis import StrictRedis
|
from redis import StrictRedis
|
||||||
redis_connection = StrictRedis()
|
redis_connection = StrictRedis()
|
||||||
|
|
||||||
|
def mocked_idoklad_token(*args, **kwargs): # pragma: no cover
|
||||||
|
class MockToken:
|
||||||
|
def __init__(self, *args,**kwargs):
|
||||||
|
self.access_token = "aap"
|
||||||
|
|
||||||
|
return MockToken()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def mocked_grpc(*args, **kwargs): # pragma: no cover
|
def mocked_grpc(*args, **kwargs): # pragma: no cover
|
||||||
class insecure_channel:
|
class insecure_channel:
|
||||||
@@ -773,6 +781,9 @@ def mocked_requests(*args, **kwargs):
|
|||||||
with open('rowers/tests/testdata/rp3_list.json','r') as infile:
|
with open('rowers/tests/testdata/rp3_list.json','r') as infile:
|
||||||
rp3workoutlist = json.load(infile)
|
rp3workoutlist = json.load(infile)
|
||||||
|
|
||||||
|
with open('rowers/tests/testdata/idoklad_default.json','r') as infile:
|
||||||
|
idokladdefault = json.load(infile)
|
||||||
|
|
||||||
rp3linkready = {'data': {'download': {'id': 591621, 'status': 'ready', 'link': 'https://rp3rowing-app.com/api/workouts/591621/download?type=csv'}}}
|
rp3linkready = {'data': {'download': {'id': 591621, 'status': 'ready', 'link': 'https://rp3rowing-app.com/api/workouts/591621/download?type=csv'}}}
|
||||||
|
|
||||||
with open('rowers/tests/testdata/example-session-strokes-with-impeller-data.json','r') as infile:
|
with open('rowers/tests/testdata/example-session-strokes-with-impeller-data.json','r') as infile:
|
||||||
@@ -1117,6 +1128,7 @@ def mocked_requests(*args, **kwargs):
|
|||||||
rp3tester = re.compile(r'.*?rp3rowing-app\.com')
|
rp3tester = re.compile(r'.*?rp3rowing-app\.com')
|
||||||
garmintester = re.compile(r'.*?garmin\.com')
|
garmintester = re.compile(r'.*?garmin\.com')
|
||||||
fakturoidtester = re.compile(r'.*?fakturoid\.cz')
|
fakturoidtester = re.compile(r'.*?fakturoid\.cz')
|
||||||
|
idokladtester = re.compile(r'.*?idoklad\.cz')
|
||||||
|
|
||||||
polarlistregex = r'.*?polaraccesslink\.com\/.*\/(\d+)$'
|
polarlistregex = r'.*?polaraccesslink\.com\/.*\/(\d+)$'
|
||||||
polarlisttester = re.compile(polarlistregex)
|
polarlisttester = re.compile(polarlistregex)
|
||||||
@@ -1487,6 +1499,43 @@ def mocked_requests(*args, **kwargs):
|
|||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
return MockResponse(c2workoutdata,200)
|
return MockResponse(c2workoutdata,200)
|
||||||
|
|
||||||
|
|
||||||
|
if idokladtester.match(args[0]):
|
||||||
|
if 'Invoices' in args[0]:
|
||||||
|
if 'Default' in args[0]:
|
||||||
|
response_data = idokladdefault
|
||||||
|
|
||||||
|
return MockResponse(response_data,200)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'Data': {
|
||||||
|
'Id': 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MockResponse(response,200)
|
||||||
|
|
||||||
|
if 'Contacts' in args[0]:
|
||||||
|
response = {
|
||||||
|
'Data': {
|
||||||
|
'Items': [
|
||||||
|
{
|
||||||
|
'Id': 1,
|
||||||
|
'url':'aap',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MockResponse(response,200)
|
||||||
|
|
||||||
|
response = [
|
||||||
|
{
|
||||||
|
'Id':1,
|
||||||
|
'url':'aap',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return MockResponse(response, 200)
|
||||||
|
|
||||||
if fakturoidtester.match(args[0]):
|
if fakturoidtester.match(args[0]):
|
||||||
if 'invoices' in args[0]:
|
if 'invoices' in args[0]:
|
||||||
response = {
|
response = {
|
||||||
|
|||||||
@@ -74,11 +74,12 @@ class BraintreeUnits(TestCase):
|
|||||||
self.p2 = PaidPlan.objects.create(price=25,paymentprocessor='braintree')
|
self.p2 = PaidPlan.objects.create(price=25,paymentprocessor='braintree')
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.fakturoid.requests.get',side_effect=mocked_requests)
|
@patch('rowers.idoklad.idoklad_token', side_effect=mocked_idoklad_token)
|
||||||
@patch('rowers.fakturoid.requests.post',side_effect=mocked_requests)
|
@patch('rowers.idoklad.requests.get',side_effect=mocked_requests)
|
||||||
|
@patch('rowers.idoklad.requests.post',side_effect=mocked_requests)
|
||||||
@patch('rowers.braintreestuff.gateway', side_effect=MockBraintreeGateway)
|
@patch('rowers.braintreestuff.gateway', side_effect=MockBraintreeGateway)
|
||||||
@patch('rowers.braintreestuff.myqueue')
|
@patch('rowers.braintreestuff.myqueue')
|
||||||
def test_process_webhook(self,mock_get,mockpost,mocked_gateway,mocked_myqueue):
|
def test_process_webhook(self,mock_token, mock_get,mockpost,mocked_gateway,mocked_myqueue):
|
||||||
n = notification()
|
n = notification()
|
||||||
res = process_webhook(n)
|
res = process_webhook(n)
|
||||||
self.assertEqual(res,1)
|
self.assertEqual(res,1)
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ description: ""
|
|||||||
|
|
||||||
|
|
||||||
@patch('rowers.views.braintreestuff.gateway', side_effect=MockBraintreeGateway)
|
@patch('rowers.views.braintreestuff.gateway', side_effect=MockBraintreeGateway)
|
||||||
@patch('rowers.fakturoid.create_invoice',side_effect=mocked_invoiceid)
|
@patch('rowers.idoklad.create_invoice',side_effect=mocked_invoiceid)
|
||||||
@patch('rowers.utils.myqueue')
|
@patch('rowers.utils.myqueue')
|
||||||
def test_purchase_trainingplan_view(self, mocked_gateway,mocked_invoiceid, mocked_myqueue):
|
def test_purchase_trainingplan_view(self, mocked_gateway,mocked_invoiceid, mocked_myqueue):
|
||||||
u = UserFactory()
|
u = UserFactory()
|
||||||
|
|||||||
@@ -752,6 +752,7 @@ urlpatterns = [
|
|||||||
views.rower_prefs_view, name='rower_prefs_view'),
|
views.rower_prefs_view, name='rower_prefs_view'),
|
||||||
re_path(r'^me/prefs/user/(?P<userid>\d+)/$',
|
re_path(r'^me/prefs/user/(?P<userid>\d+)/$',
|
||||||
views.rower_simpleprefs_view, name='rower_simpleprefs_view'),
|
views.rower_simpleprefs_view, name='rower_simpleprefs_view'),
|
||||||
|
re_path(r'^me/idokladauthorize/$', views.rower_idoklad_authorize, name='rower_idoklad_authorize'),
|
||||||
re_path(r'^me/rojaboauthorize/$', views.rower_rojabo_authorize,
|
re_path(r'^me/rojaboauthorize/$', views.rower_rojabo_authorize,
|
||||||
name='rower_rojabo_authorize'),
|
name='rower_rojabo_authorize'),
|
||||||
re_path(r'^me/polarauthorize/$', views.rower_polar_authorize,
|
re_path(r'^me/polarauthorize/$', views.rower_polar_authorize,
|
||||||
|
|||||||
@@ -8,6 +8,72 @@ from django.core.mail import EmailMessage
|
|||||||
|
|
||||||
from rowers import credits
|
from rowers import credits
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def rower_idoklad_authorize(request):
|
||||||
|
state=str(uuid4())
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"client_id":IDOKLAD_CLIENT_ID,
|
||||||
|
"response_type": "code",
|
||||||
|
"redirect_uri": IDOKLAD_REDIRECT_URI,
|
||||||
|
"scope": "idoklad_api offline_access",
|
||||||
|
}
|
||||||
|
|
||||||
|
url = "https://identity.idoklad.cz/server/connect/authorize?"+urllib.parse.urlencode(params)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def process_idokladcallback(request):
|
||||||
|
dologging('idoklad.log',' /rowers/idokladcallback/')
|
||||||
|
|
||||||
|
try:
|
||||||
|
code = request.GET['code']
|
||||||
|
except KeyError:
|
||||||
|
error = request.GET['error']
|
||||||
|
messages.error(request,error)
|
||||||
|
return HttpResponseRedirect(reverse('workouts_view'))
|
||||||
|
|
||||||
|
post_data = {
|
||||||
|
'grant_type': "authorization_code",
|
||||||
|
'client_id': IDOKLAD_CLIENT_ID,
|
||||||
|
'client_secret': IDOKLAD_CLIENT_SECRET,
|
||||||
|
'scope': 'idoklad_api offline_access',
|
||||||
|
'code': code,
|
||||||
|
'redirect_uri': IDOKLAD_REDIRECT_URI,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
}
|
||||||
|
|
||||||
|
base_url = 'https://identity.idoklad.cz/server/connect/token'
|
||||||
|
|
||||||
|
response = requests.post(base_url, data=post_data, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
result = response.json()
|
||||||
|
try:
|
||||||
|
t = iDokladToken.objects.get(id=1)
|
||||||
|
t.acces_token = result['access_token'],
|
||||||
|
t.refresh_token = result['refresh_token']
|
||||||
|
t.expires_in = result['expires_in']
|
||||||
|
t.id_token = result['id_token']
|
||||||
|
t.save()
|
||||||
|
except iDokladToken.DoesNotExist:
|
||||||
|
t = iDokladToken(
|
||||||
|
access_token = result['access_token'],
|
||||||
|
refresh_token = result['refresh_token'],
|
||||||
|
expires_in = result['expires_in'],
|
||||||
|
)
|
||||||
|
t.save()
|
||||||
|
messages.info(request,"Token refreshed and stored")
|
||||||
|
else:
|
||||||
|
messages.error(request,"Error")
|
||||||
|
|
||||||
|
url = reverse('rower_exportsettings_view')
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def braintree_webhook_view(request):
|
def braintree_webhook_view(request):
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ from rowers.models import ( RowerPowerForm, RowerHRZonesForm, SimpleRowerPowerFo
|
|||||||
IndoorVirtualRaceForm, PlannedSessionCommentForm, Alert,
|
IndoorVirtualRaceForm, PlannedSessionCommentForm, Alert,
|
||||||
Condition, StaticChartRowerForm, FollowerForm,
|
Condition, StaticChartRowerForm, FollowerForm,
|
||||||
VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm,
|
VirtualRaceAthleteForm, InstantPlanForm, DataRowerForm,
|
||||||
StepEditorForm, )
|
StepEditorForm, iDokladToken )
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
FavoriteForm, BaseFavoriteFormSet, SiteAnnouncement, BasePlannedSessionFormSet,
|
FavoriteForm, BaseFavoriteFormSet, SiteAnnouncement, BasePlannedSessionFormSet,
|
||||||
get_course_timezone, BaseConditionFormSet,
|
get_course_timezone, BaseConditionFormSet,
|
||||||
@@ -226,6 +226,7 @@ from rowsandall_app.settings import (
|
|||||||
RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET,
|
RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET,
|
||||||
NK_REDIRECT_URI, NK_CLIENT_ID, NK_CLIENT_SECRET,
|
NK_REDIRECT_URI, NK_CLIENT_ID, NK_CLIENT_SECRET,
|
||||||
ROJABO_REDIRECT_URI, ROJABO_CLIENT_ID, ROJABO_CLIENT_SECRET,
|
ROJABO_REDIRECT_URI, ROJABO_CLIENT_ID, ROJABO_CLIENT_SECRET,
|
||||||
|
IDOKLAD_REDIRECT_URI, IDOKLAD_CLIENT_ID, IDOKLAD_CLIENT_SECRET,
|
||||||
)
|
)
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ AUTHENTICATION_BACKENDS = (
|
|||||||
#'rules.permissions.ObjectPermissionBackend',
|
#'rules.permissions.ObjectPermissionBackend',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CSRF_TRUSTED_ORIGINS = ['https://rowsandall.com', 'https://www.rowsandall.com', 'http://localhost', 'https://dunav.ngrok.io']
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.common.BrokenLinkEmailsMiddleware',
|
'django.middleware.common.BrokenLinkEmailsMiddleware',
|
||||||
@@ -598,6 +600,16 @@ try:
|
|||||||
except KeyError: # pragma: no cover
|
except KeyError: # pragma: no cover
|
||||||
FAKTUROID_SLUG = ''
|
FAKTUROID_SLUG = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
IDOKLAD_CLIENT_ID = CFG['idoklad_client_id']
|
||||||
|
IDOKLAD_CLIENT_SECRET = CFG['idoklad_client_secret']
|
||||||
|
IDOKLAD_REDIRECT_URI = CFG['idoklad_redirect_uri']
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
IDOKLAD_CLIENT_ID = ''
|
||||||
|
IDOKLAD_CLIENT_SECRET = ''
|
||||||
|
IDOKLAD_REDIRECT_URI = ''
|
||||||
|
|
||||||
|
|
||||||
# ID obfuscation
|
# ID obfuscation
|
||||||
try:
|
try:
|
||||||
OPAQUE_SECRET_KEY = CFG['opaque_secret_key']
|
OPAQUE_SECRET_KEY = CFG['opaque_secret_key']
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ urlpatterns += [
|
|||||||
re_path(r'^tp\_callback', rowersviews.rower_process_tpcallback),
|
re_path(r'^tp\_callback', rowersviews.rower_process_tpcallback),
|
||||||
re_path(r'^rp3\_callback', rowersviews.rower_process_rp3callback),
|
re_path(r'^rp3\_callback', rowersviews.rower_process_rp3callback),
|
||||||
re_path(r'^twitter\_callback', rowersviews.rower_process_twittercallback),
|
re_path(r'^twitter\_callback', rowersviews.rower_process_twittercallback),
|
||||||
|
re_path(r'^idoklad\_callback', rowersviews.process_idokladcallback),
|
||||||
re_path(r'^i18n/', include('django.conf.urls.i18n')),
|
re_path(r'^i18n/', include('django.conf.urls.i18n')),
|
||||||
re_path(r'^tz_detect/', include('tz_detect.urls')),
|
re_path(r'^tz_detect/', include('tz_detect.urls')),
|
||||||
re_path(r'^logo/', logoview),
|
re_path(r'^logo/', logoview),
|
||||||
|
|||||||
Reference in New Issue
Block a user