Verified Commit bdf47e1b authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg
Browse files

Start work on Conscribo member sync

parent 81642569
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, ResultException
from utils.conscribo.objects import Command as ApiCommand
logger = logging.getLogger(__name__)
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
dest='dry-run',
default=False,
help='Dry run instead of syncing for real',
)
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()
}
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,
'einddatum_lidmaatschap':
date(member.current_membership.until, 'Y-m-d'),
'e_mailadres': member.email,
'straatnaam': profile.address_street,
'postcode': profile.address_postal_code,
'plaats': profile.address_city,
'land': 'Nederland',
'bankrekeningnummer': {
'name': '',
'bic': '',
'iban': profile.bank_account,
},
}
replace_commands.append(ApiCommand(
command='ReplaceRelation',
entityType='lid',
fields=fields,
code=code,
))
delete_commands = []
for website_id, code in current_relations.items():
delete_commands.append(ApiCommand(
command='DeleteRelation',
entityType='lid',
code=code,
website_id=website_id # not used by server
))
delete_responses = api.multi_request(delete_commands)
for response in delete_responses:
if not response.success:
logger.debug(response.notifications)
website_id = delete_commands[
response.request_sequence].data.get('website_id')
code = delete_commands[
response.request_sequence].data.get('code')
member = Member.objects.get(pk=website_id)
fields = {
'website_id': member.pk,
'voornaam': member.first_name,
'naam': member.last_name,
'einddatum_lidmaatschap':
date(member.latest_membership.until, 'Y-m-d'),
'e_mailadres': '',
'straatnaam': '',
'postcode': '',
'plaats': '',
'land': '',
'bankrekeningnummer': {
'name': '',
'bic': '',
'iban': '',
},
}
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)
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)
......@@ -283,6 +283,11 @@ MAILINGLIST_API_SECRET = ''
# Members Sentry API key
MEMBERS_SENTRY_API_SECRET = ''
# Conscribo settings
CONSCRIBO_ACCOUNT = ''
CONSCRIBO_USER = ''
CONSCRIBO_PASSWORD = ''
# Activemembers NextCloud API key
ACTIVEMEMBERS_NEXTCLOUD_API_SECRET = ''
......
import requests
from utils.conscribo.objects import *
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:
return 'not implemented'
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)
Supports Markdown
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