334 lines
13 KiB
Python
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
|