passerelle/passerelle/contrib/solis_apa/models.py

334 lines
13 KiB
Python

# Passerelle - uniform access to data and services
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import json
from django.db import models
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse
from passerelle.base.models import BaseResource
from passerelle.compat import json_loads
from passerelle.contrib.solis_apa import conciliation, suivi, integration
HEADERS = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
APPLICATION = 'AsgTeleprocedureApa14'
class SolisAPA(BaseResource):
base_url = models.CharField(max_length=128, blank=False,
verbose_name=_('url'))
verify_cert = models.BooleanField(default=True,
verbose_name=_('Check HTTPS Certificate validity'))
username = models.CharField(max_length=128, blank=True,
verbose_name=_('Username'))
password = models.CharField(max_length=128, blank=True,
verbose_name=_('Password'))
keystore = models.FileField(upload_to='solis_apa', null=True, blank=True,
verbose_name=_('Keystore'),
help_text=_('Certificate and private key in PEM format'))
category = _('Business Process Connectors')
class Meta:
verbose_name = _('Solis (legacy)')
@classmethod
def get_verbose_name(cls):
return cls._meta.verbose_name
def check_status(self):
self.get_communes(query='', use_cache=False)
def _check_requests_response(self, response):
try:
ret = response.json()
return ret
except(ValueError) as e:
raise Exception('Response content is not a valid JSON')
def get_resource_url(self, uri):
return urlparse.urljoin(self.base_url,uri)
# Referentials methods
def _referential(self, referential, keys=True, order_by=False,
stop_on_error=False, attributes=[], **filters):
uri = 'referential?referential=%s' % referential
url = self.get_resource_url(uri)
data = {
'ReferentialOptions': {
'processKeys': keys,
'processOrderBy': order_by,
'stopOnError': stop_on_error,
}}
if filters:
solis_filters = []
for k,v in filters.items():
solis_filters.append({
'key':k,
'value': v
})
data['ReferentialOptions']['Filters'] = {'Filter': solis_filters}
if attributes:
data['ReferentialOptions']['Attributes'] = {
"referential": [
{
"schema": "stdr",
"table": referential,
"field": attributes,
}
]
}
data = json.dumps(data)
response = self.requests.post(url, data=data, headers=HEADERS)
if response.status_code != 200:
raise ValueError('referential ws: error code %d' % response.status_code)
ret = self._check_requests_response(response)
l = []
count = int(ret['ReferentialOutputWS']['Entries']['@count'])
name = ret['ReferentialOutputWS']['Entries']['@name']
if count:
entries = ret['ReferentialOutputWS']['Entries']['Entry']
if type(entries) is not list:
entries = [entries]
l += entries
return {'results': l, 'name': name, 'count': count, 'error': False}
def _conciliation(self, config, **data):
uri = 'conciliation'
url = self.get_resource_url(uri)
name = config['block']['name'].lower()
data = json.dumps(conciliation.conciliation_payload(config, **data))
response = self.requests.post(url, data=data, headers=HEADERS)
if response.status_code != 200:
raise ValueError('conciliation ws: error code %d' % response.status_code)
ret = self._check_requests_response(response)
l = []
count = int(ret['ConciliationOutputWS']['Results']['@count'])
if count:
results = ret['ConciliationOutputWS']['Results']['ResultsByAffinity']
if type(results) is not list:
results = [results]
for r in results:
affinity = r['@affinity']
entities = r['Entities']['entity']
if type(entities) is not list:
entities = [entities]
for e in entities:
e = conciliation.conciliation_output2dict(config, e)
e['@affinity'] = affinity
l.append(e)
return {'results': l, 'name': name, 'count': count, 'error': False}
def get_communes(self, query, code_dep=14, use_cache=True):
if query:
query = query.lower()
if re.match('^\d\d', query):
# query est le debut d'un code postal
code_dep = query[:2]
if not code_dep:
return {}
cache_key = 'solis-liste-communes-%s' % code_dep
ref = cache.get(cache_key)
if not ref or not use_cache:
ref = self._referential(referential='commune',
attributes=['cp_lieu'],
code_dep=code_dep)
if use_cache:
cache.set(cache_key, ref, 60*60)
villes = ref.get('results')
ret = []
for v in villes:
# {u'Attributes': {u'Attribute': {u'id': u'stdr.commune.cp_lieu',
# u'value': 14210}}, u'Keys': {u'Key': [{u'id':
# u'stdr.commune.code_a_com', u'value': 771}, {u'id':
# u'stdr.commune.code_dep', u'value': 14}]}, u'id':
# u'commune-14-771', u'value': u'NEUILLY LE MALHERBE'},
#
# However, the lack of some informations has already been observed,
# so there are some test/continue here
if ('value' not in v) or ('id' not in v):
continue
attrs = {}
for attr in v['Attributes']['Attribute']:
attrs[attr['id']] = attr['value']
if 'stdr.commune.cp_lieu' not in attrs:
continue
text = '%0.5d %s' % (attrs['stdr.commune.cp_lieu'], v['value'].strip())
if query and not query in text.lower():
continue
ret.append({'id': v['id'], 'text': text})
return ret
def _cache(self, key, value=None):
if value:
cache.set(key, value, 60*60)
return True
cache_data = cache.get(key)
if cache_data:
return cache_data
def get_lieux(self, q, commune, departement):
# si commune est un code solis de la forme commune-dep-com
if commune and commune.startswith('commune-'):
x, departement, commune = commune.split('-')
call = self._conciliation(conciliation.CONCILIATION_ADRESSE,
commune=commune, departement=departement,
lieu='%%%s%%' % q)
lieux = call.get('results')
ret = []
for l in lieux:
# '@affinity': u'5',
# 'CodeDepartement/@V': u'',
# 'CodeLieu/@V': u'0110',
# 'CodePostal/@V': u'14000',
# 'Commune/NomCom/@V': u'CAEN',
# 'Commune/PK/CodeCommune/@V': u'118',
# 'NatureLieu/@Lc': u'RUE',
# 'NomLieu/@V': u'DU BEAU SITE'
for k,v in l.items():
l[k] = v.strip()
ret.append({
'id': '%(CodeLieu/@V)s' % l,
'text': '%(NatureLieu/@Lc)s %(NomLieu/@V)s' % l,
'affinity': '%(@affinity)s' % l,
})
return ret
def get_homonymes(self, nom, prenom, dn):
if dn:
dn = dn[6:]+'-'+dn[3:5]+'-'+dn[:2]
call = self._conciliation(conciliation.CONCILIATION_INDIVIDU,
nom=nom, prenom=prenom, dn=dn)
else:
call = self._conciliation(conciliation.CONCILIATION_INDIVIDU_SANS_DN,
nom=nom, prenom=prenom)
individus = call.get('results')
ret = []
for i in individus:
# i = {'@affinity': u'3',
# 'Dossier/Adresse/Commune/NomCom/@V': u'ST JULIEN EN GENEVOIS',
# 'Dossier/Adresse/ComplementLieu/@V': u'ROUTE DE THOIRY',
# 'Dossier/Adresse/CpLieu/@V': u'74160',
# 'Dossier/Adresse/NatureLieu/@Lc': u'',
# 'Dossier/Adresse/NomLieu/@V': u'.',
# 'Dossier/Adresse/NumeroLieu/@V': u'39',
# 'Dossier/PK/IndexDossier/@V': u'162438',
# 'EtatCivil/DateNaissance/@V': u'1933-08-28',
# 'EtatCivil/Nom/@V': u'DUPONT',
# 'EtatCivil/NomJeuneFille/@V': u'BUATHIER',
# 'EtatCivil/Prenom/@V': u'JEANNE',
# 'PK/IndexIndividu/@V': u'208359'},
for k,v in i.items():
i[k] = v.strip()
njf = i['EtatCivil/NomJeuneFille/@V']
if njf:
i['EtatCivil/NomJeuneFille/@V'] = u' (%s)' % njf
if not i['EtatCivil/DateNaissance/@V']:
i['EtatCivil/DateNaissance/@V'] = u'date de naissance inconnue'
ret.append({
'id': '%(PK/IndexIndividu/@V)s' % i,
'text': ('%(EtatCivil/Nom/@V)s%(EtatCivil/NomJeuneFille/@V)s %(EtatCivil/Prenom/@V)s' + \
' - %(EtatCivil/DateNaissance/@V)s' + \
' - %(Dossier/Adresse/CpLieu/@V)s %(Dossier/Adresse/Commune/NomCom/@V)s') % i,
'affinity': '%(@affinity)s' % i,
})
ret.sort(lambda x,y: cmp(y['affinity'],x['affinity']))
return ret
def _process_common_ref(self, ref_name, q=None):
cache_key = 'solis-apa-%s' %ref_name.replace(' ','-')
ref = self._cache(cache_key)
if not ref:
ref = self._referential(ref_name)
self._cache(cache_key, ref)
ret = []
for result in ref.get('results'):
if result.get('value') and (not q or q.lower() in result['value'].lower()):
ret.append({'id': result['id'], 'text': result['value']})
return ret
def get_referential(self, reference_name, q=None):
return self._process_common_ref(reference_name.replace('-',' '), q=q)
def get_suivi(self, suivi_type, datedebut, datefin):
resource = {
'visite': 'ExportSuiviVisite',
'plan-aide': 'ExportSuiviPlanAide',
'presentation-commission': 'ExportSuiviPresentationCommission',
'decision-commission': 'ExportSuiviDecisionCommission'
}
uri = 'exportFlow?flow={}&application={}'.format(resource[suivi_type],
APPLICATION)
url = self.get_resource_url(uri)
payload = suivi.render_payload(suivi_type, datedebut, datefin)
payload = json.dumps(payload)
response = self.requests.post(url, data=payload, headers=HEADERS, timeout=300)
if response.status_code != 200:
raise ValueError('suivi %s ws: error code %d' %(suivi_type, response.status_code))
response = self._check_requests_response(response)
output = suivi.suivi_output(suivi_type, response)
return output
def import_flow(self, data):
uri = 'importFlow?flow=ImportIntegrationDemande&application=%s' %APPLICATION
url = self.get_resource_url(uri)
data = {'ImportInputWSDemandeApa': integration.build_message(json_loads(data))}
data = json.dumps(data)
self.logger.debug('Demande APA: %s' % data, extra={'solis_apa_demande': data})
response = self.requests.post(url, data=data, headers=HEADERS)
if response.status_code != 200:
raise ValueError('integration ws: error code %d' %(response.status_code))
response = self._check_requests_response(response)
ret = {}
for x in response['ImportIdResults']['Items']:
ret[x['key']] = x['value']
return ret