Commit d7cd56e3 authored by Joren Vrancken's avatar Joren Vrancken

Merge branch 'feature/concrexit-sync' into 'master'

Conscribo member sync

Closes #790

See merge request !1132
parents a1fc7165 3eb3e02d
utils.conscribo package
=======================
.. automodule:: utils.conscribo
:members:
:undoc-members:
:show-inheritance:
Submodules
----------
utils.conscribo.api module
--------------------------
.. automodule:: utils.conscribo.api
:members:
:undoc-members:
:show-inheritance:
utils.conscribo.objects module
------------------------------
.. automodule:: utils.conscribo.objects
:members:
:undoc-members:
:show-inheritance:
......@@ -11,6 +11,7 @@ Subpackages
.. toctree::
utils.conscribo
utils.management
utils.templatetags
......
import logging
from django.conf import settings
from django.core.management.base import BaseCommand
from django.template.defaultfilters import date
from requests import HTTPError
from members.models import Member
from utils.conscribo.api import ConscriboApi
from utils.conscribo.objects import Command as ApiCommand, ResultException
logger = logging.getLogger(__name__)
class Command(BaseCommand):
def handle(self, *args, **options):
api = ConscriboApi(settings.CONSCRIBO_ACCOUNT,
settings.CONSCRIBO_USER,
settings.CONSCRIBO_PASSWORD)
try:
relations_response = api.single_request(
'listRelations',
entityType='lid',
requestedFields={
'fieldName': ['website_id', 'code']
},
)
relations_response.raise_for_status()
relations_response = relations_response.data
current_relations = {}
if len(relations_response.get('relations')) > 0:
current_relations = {
int(r.get('website_id')): r.get('code', None)
for r in relations_response.get('relations').values()
if r.get('website_id', '') != ''
}
replace_commands = []
for member in Member.current_members.all():
code = current_relations.pop(member.pk, None)
profile = member.profile
fields = {
'website_id': member.pk,
'voornaam': member.first_name,
'naam': member.last_name[:100], # Conscribo maxlength: 100
'einddatum_lidmaatschap':
date(member.current_membership.until, 'Y-m-d'),
'e_mailadres': member.email,
'eerste_adresregel': profile.address_street,
'tweede_adresregel': profile.address_street2,
'postcode': profile.address_postal_code,
'plaats': profile.address_city,
'land': 'Nederland',
'bankrekeningnummer': {
'name': f'${profile.initials} ${member.last_name}',
'bic': '',
'iban': profile.bank_account,
},
}
replace_commands.append(ApiCommand(
command='ReplaceRelation',
entityType='lid',
fields=fields,
code=code,
))
replace_responses = api.multi_request(replace_commands)
for response in replace_responses:
if not response.success:
logger.debug(response.notifications)
delete_commands = []
for code in current_relations.values():
delete_commands.append(ApiCommand(
command='DeleteRelation',
entityType='lid',
code=code,
))
delete_responses = api.multi_request(delete_commands)
for response in delete_responses:
if not response.success:
logger.debug(response.notifications)
except HTTPError as e:
logger.error('HTTP error syncing relations to Conscribo', e)
except ResultException as e:
logger.error('Server error syncing relations to Conscribo', e)
......@@ -90,6 +90,11 @@ if not (FIREBASE_CREDENTIALS == '{}'):
FIREBASE_CREDENTIALS = base64.urlsafe_b64decode(FIREBASE_CREDENTIALS)
FIREBASE_CREDENTIALS = json.loads(FIREBASE_CREDENTIALS)
# Conscribo settings
CONSCRIBO_ACCOUNT = os.environ.get('CONSCRIBO_ACCOUNT', '')
CONSCRIBO_USER = os.environ.get('CONSCRIBO_USER', '')
CONSCRIBO_PASSWORD = os.environ.get('CONSCRIBO_PASSWORD', '')
if os.environ.get('DJANGO_SSLONLY'):
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
......
......@@ -276,6 +276,11 @@ BOARD_NOTIFICATION_ADDRESS = 'info@thalia.nu'
# Partners notification email
PARTNER_EMAIL = "samenwerking@thalia.nu"
# Conscribo settings
CONSCRIBO_ACCOUNT = ''
CONSCRIBO_USER = ''
CONSCRIBO_PASSWORD = ''
# Mailinglist API key
MAILINGLIST_API_SECRET = ''
......
import requests
from utils.conscribo.objects import Request, Result
class ConscriboApi:
def __init__(self, account, username, password):
self._session = requests.session()
self._endpoint = (f'https://secure.conscribo.nl'
f'/{account}/request.json')
self._headers = {"X-Conscribo-API-Version": "0.20161212"}
self.authenticate(username, password)
def single_request(self, command, **params):
response = self._session.post(
self._endpoint,
Request.single(command, **params).json(),
headers=self._headers
)
response.raise_for_status()
result = Result.single(response.json())
return result
def multi_request(self, commands):
if len(commands) == 1:
return [self.single_request(commands[0])]
response = self._session.post(
self._endpoint,
Request.multi(commands).json(),
headers=self._headers
)
response.raise_for_status()
return Result.multi(response.json())
def authenticate(self, username, password):
result = self.single_request(
command='authenticateWithUserAndPass',
userName=username,
passPhrase=password
)
self._headers.update({
"X-Conscribo-SessionId": result.data.get('sessionId', None)
})
import json
class _JsonSerializable(object):
@property
def data(self) -> object:
raise NotImplementedError
def json(self) -> str:
return json.dumps(self.data)
def __str__(self) -> str:
return self.json()
class Command(_JsonSerializable):
def __init__(self, command, **params) -> None:
self._command = command
self._params = params
@property
def data(self) -> dict:
return {
'command': self._command,
**self._params,
}
class Request(_JsonSerializable):
def __init__(self, commands) -> None:
self._commands = commands
@property
def data(self) -> dict:
if len(self._commands) == 1:
return {
'request': self._commands[0].data
}
return {
'requests': {
'request': [{**v.data, 'requestSequence': str(k)}
for k, v in enumerate(self._commands)]
}
}
@staticmethod
def single(command, **params) -> _JsonSerializable:
if isinstance(command, Command):
return Request([command])
return Request([Command(command, **params)])
@staticmethod
def multi(commands) -> _JsonSerializable:
return Request(commands)
class ResultException(Exception):
def __init__(self, notifications):
msg = "Error occurred on server:\n" + "\n".join(notifications)
super(ResultException, self).__init__(msg)
class Result(object):
def __init__(self, data) -> None:
self._data = data
self._success = self._data.pop('success', False)
self._request_sequence = self._data.pop('requestSequence', 0)
self._notifications = self._data.pop('notifications', {
'notification': []
}).get('notification')
@staticmethod
def single(data):
return Result(data.get('result', {}))
@staticmethod
def multi(data):
return [Result(result) for result in
data.get('results', {'result': []}).get('result', None)]
@property
def success(self):
return self._success
@property
def notifications(self):
return self._notifications
@property
def request_sequence(self):
return self._request_sequence
@property
def data(self):
return self._data
def raise_for_status(self):
if not self.success:
raise ResultException(self.notifications)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment