diff --git a/passerelle/apps/atal/models.py b/passerelle/apps/atal/models.py
index 84d5552d..0d51eeb9 100644
--- a/passerelle/apps/atal/models.py
+++ b/passerelle/apps/atal/models.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2019 Entr'ouvert
#
@@ -16,74 +14,25 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+import base64
from django.db import models
from django.utils.six.moves import urllib
from django.utils.translation import ugettext_lazy as _
import lxml.etree
from zeep import helpers
+from zeep.exceptions import Fault
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
+from . import schemas
-INSERT_DEMANDE_BY_TYPE_SCHEMA = {
- "$schema": "http://json-schema.org/draft-03/schema#",
- "title": "",
- "description": "",
- "type": "object",
- "properties": {
- "contact_nom": {
- "description": "Nom du contact",
- "required": True
- },
- "contact_tel": {
- "description": "Téléphone du contact",
- "type": "string",
- },
- "contact_email": {
- "description": "Email du contact",
- "type": "string",
- },
- "contact_adresse": {
- "description": "Adresse du contact",
- "type": "string",
- },
- "demande_objet": {
- "description": "Objet de la demande",
- "type": "string",
- },
- "demande_lieu": {
- "description": "Lieu de la demande",
- "type": "string",
- },
- "demande_description": {
- "description": "Description de la demande",
- "type": "string",
- },
- "remote_adresse": {
- "description": "",
- "type": "string"
- },
- "code_equipement": {
- "description": "Code de l'équipement",
- "type": "string"
- },
- "code_service_demandeur": {
- "description": "Code du service demandeur",
- "type": "string"
- },
- "date_souhaite": {
- "description": "Date souhaitée",
- "type": "string"
- },
- "type_demande": {
- "description": "Type demande",
- "type": "string"
- }
- }
-}
+def process_response(demande_number):
+ if not demande_number.startswith('DIT'):
+ raise APIError(demande_number)
+ return {'data': {'demande_number': demande_number}}
class ATALConnector(BaseResource):
@@ -98,7 +47,10 @@ class ATALConnector(BaseResource):
def _soap_call(self, wsdl, method, **kwargs):
wsdl_url = urllib.parse.urljoin(self.base_soap_url, '%s?wsdl' % wsdl)
client = self.soap_client(wsdl_url=wsdl_url)
- return getattr(client.service, method)(**kwargs)
+ try:
+ return getattr(client.service, method)(**kwargs)
+ except Fault as e:
+ raise APIError(unicode(e))
def _basic_ref(self, wsdl, method):
soap_res = self._soap_call(wsdl=wsdl, method=method)
@@ -107,15 +59,15 @@ class ATALConnector(BaseResource):
res.append({'id': elem.code, 'text': elem.libelle})
return {'data': res}
- @endpoint(methods=['get'], perm='can_access')
+ @endpoint(methods=['get'], perm='can_access', name='get-type-activite')
def get_type_activite(self, request):
return self._basic_ref('VilleAgileService', 'getTypeActivite')
- @endpoint(methods=['get'], perm='can_access')
+ @endpoint(methods=['get'], perm='can_access', name='get-type-de-voie')
def get_type_de_voie(self, request):
return self._basic_ref('VilleAgileService', 'getTypeDeVoie')
- @endpoint(methods=['get'], perm='can_access')
+ @endpoint(methods=['get'], perm='can_access', name='get-types-equipement')
def get_types_equipement(self, request):
soap_res = self._soap_call(wsdl='VilleAgileService', method='getTypesEquipement')
tree = lxml.etree.fromstring(soap_res.encode('utf-8')).getroottree()
@@ -126,44 +78,124 @@ class ATALConnector(BaseResource):
return {'data': res}
@endpoint(
- perm='can_access',
+ perm='can_access', name='insert-action-comment',
post={
- 'description': _('Insert Demande By Type'),
+ 'description': 'Insert action comment',
'request_body': {
'schema': {
- 'application/json': INSERT_DEMANDE_BY_TYPE_SCHEMA
+ 'application/json': schemas.INSERT_ACTION_COMMENT
}
}
}
)
- def insert_demande_by_type(self, request, post_data):
+ def insert_action_comment(self, request, post_data):
demande_number = self._soap_call(
- wsdl='DemandeService', method='insertDemandeByType',
- contactNom=post_data['contact_nom'],
- contactTelephone=post_data['contact_telephone'],
- contactCourriel=post_data['contact_email'],
- contactAdresse=post_data['contact_adresse'], demandeObjet=post_data['demande_objet'],
- demandeLieu=post_data['demande_lieu'],
- demandeDescription=post_data['demande_description'],
- remoteAddress=post_data['remote_adresse'], codeEquipement=post_data['code_equipement'],
- codeServiceDemandeur=post_data['code_service_demandeur'],
- dateSouhaitee=post_data['date_souhaite'], typeDemande=post_data['type_demande']
+ wsdl='DemandeService', method='insertActionComment',
+ numeroDemande=post_data['numero_demande'],
+ commentaire=post_data['commentaire']
)
- return {'data': {'demande_number': demande_number}}
+ return process_response(demande_number)
+
+ @endpoint(
+ perm='can_access', name='insert-demande-complet-by-type',
+ post={
+ 'description': 'Insert demande complet by type',
+ 'request_body': {
+ 'schema': {
+ 'application/json': schemas.INSERT_DEMANDE_COMPLET_BY_TYPE
+ }
+ }
+ }
+ )
+ def insert_demande_complet_by_type(self, request, post_data):
+ data = {}
+ for recv, send in [
+ ('type_demande', 'typeDemande'),
+ ('code_service_demandeur', 'codeServiceDemandeur'),
+ ('date_saisie', 'dateSaisie'),
+ ('date_demande', 'dateDemande'),
+ ('date_souhaite', 'dateSouhaitee'),
+ ('date_butoir', 'dateButoir'),
+ ('contact_civilite', 'contactCivilite'),
+ ('contact_nom', 'contactNom'),
+ ('contact_prenom', 'contactPrenom'),
+ ('contact_tel', 'contactTelephone'),
+ ('contact_mobile', 'contactMobile'),
+ ('contact_email', 'contactCourriel'),
+ ('contact_info_compl', 'contactInfoCompl'),
+ ('demande_type_support', 'demandeTypeDeSupport'),
+ ('contact_adresse', 'contactAdresse'),
+ ('contact_adresse_compl', 'contactAdresseCompl'),
+ ('contact_code_postal', 'contactCodePostal'),
+ ('contact_ville', 'contactVille'),
+ ('contact_organisme', 'contactOrganisme'),
+ ('contact_titre', 'contactTitre'),
+ ('code_equipement', 'codeEquipement'),
+ ('code_mairie_equipement', 'codeMairieEquipement'),
+ ('code_sig_equipement', 'codeSIGEquipement'),
+ ('code_collectivite_equipement', 'codeCollectiviteEquipement'),
+ ('code_quartier_equipement', 'codeQuartierEquipement'),
+ ('code_type_equipement', 'codeTypeEquipement'),
+ ('demande_lieu', 'demandeLieu'),
+ ('coord_x', 'coordX'),
+ ('coord_y', 'coordY'),
+ ('demande_priorite', 'demandePriorite'),
+ ('demande_objet', 'demandeObjet'),
+ ('demande_description', 'demandeDescription'),
+ ('demande_commentaire', 'demandeCommentaire'),
+ ('demande_mots_cles', 'demandeMotsCles'),
+ ('code_thematique', 'codeThematiqueATAL'),
+ ('code_priorite', 'codePrioriteATAL'),
+ ('demande_thematique', 'demandeThematique'),
+ ('code_projet', 'codeProjetATAL'),
+ ]:
+ if recv in post_data:
+ data[send] = post_data[recv]
+
+ demande_number = self._soap_call(
+ wsdl='DemandeService', method='insertDemandeCompletByType', **data
+ )
+ return process_response(demande_number)
@endpoint(
methods=['get'], perm='can_access', example_pattern='{demande_number}/',
- pattern='^(?P\w+)/$',
+ pattern='^(?P\w+)/$', name='retrieve-etat-travaux',
parameters={
'demande_number': {
'description': _('Demande number'), 'example_value': 'DIT18050001'
}
}
)
- def retrieve_details_demande(self, request, demande_number, **kwargs):
- if not demande_number:
- raise APIError('A demande_number parameter must be specified')
+ def retrieve_etat_travaux(self, request, demande_number):
soap_res = self._soap_call(
- wsdl='DemandeService', method='retrieveDetailsDemande',
- demandeNumberParam=demande_number)
+ wsdl='DemandeService', method='retrieveEtatTravaux',
+ numero=demande_number)
return {'data': helpers.serialize_object(soap_res)}
+
+ @endpoint(
+ perm='can_access',
+ post={
+ 'description': 'Upload a file',
+ 'request_body': {
+ 'schema': {
+ 'application/json': schemas.UPLOAD
+ }
+ }
+ }
+ )
+ def upload(self, request, post_data):
+ try:
+ content = base64.b64decode(post_data['file']['content'])
+ except TypeError:
+ raise APIError('Invalid base64 string')
+
+ data = {
+ 'donneesFichier': content,
+ 'numeroDemande': post_data['numero_demande'],
+ 'nomFichier': post_data['nom_fichier']
+ }
+ self._soap_call(
+ wsdl='ChargementPiecesJointesService', method='upload',
+ **data
+ )
+ return {}
diff --git a/passerelle/apps/atal/schemas.py b/passerelle/apps/atal/schemas.py
new file mode 100644
index 00000000..7dea8346
--- /dev/null
+++ b/passerelle/apps/atal/schemas.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2019 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 .
+
+
+INSERT_DEMANDE_COMPLET_BY_TYPE = {
+ '$schema': 'http://json-schema.org/draft-03/schema#',
+ 'type': 'object',
+ 'additionalProperties': False,
+ 'properties': {
+ 'type_demande': {
+ 'type': 'string',
+ 'required': True,
+ },
+ 'code_service_demandeur': {
+ 'type': 'string',
+ },
+ 'date_saisie': {
+ 'type': 'string',
+ },
+ 'date_demande': {
+ 'type': 'string',
+ },
+ 'date_souhaite': {
+ 'type': 'string',
+ },
+ 'date_butoir': {
+ 'type': 'string',
+ },
+ 'contact_civilite': {
+ 'type': 'string',
+ },
+ 'contact_nom': {
+ 'type': 'string',
+ },
+ 'contact_prenom': {
+ 'type': 'string',
+ },
+ 'contact_tel': {
+ 'type': 'string',
+ },
+ 'contact_mobile': {
+ 'type': 'string',
+ },
+ 'contact_email': {
+ 'type': 'string',
+ },
+ 'contact_info_compl': {
+ 'type': 'string',
+ },
+ 'demande_type_support': {
+ 'type': 'string',
+ },
+ 'contact_adresse': {
+ 'type': 'string',
+ },
+ 'contact_adresse_compl': {
+ 'type': 'string',
+ },
+ 'contact_code_postal': {
+ 'type': 'string',
+ },
+ 'contact_ville': {
+ 'type': 'string',
+ },
+ 'contact_organisme': {
+ 'type': 'string',
+ },
+ 'contact_titre': {
+ 'type': 'string',
+ },
+ 'code_equipement': {
+ 'type': 'string',
+ },
+ 'code_mairie_equipement': {
+ 'type': 'string',
+ },
+ 'code_sig_equipement': {
+ 'type': 'string',
+ },
+ 'code_collectivite_equipement': {
+ 'type': 'string',
+ },
+ 'code_quartier_equipement': {
+ 'type': 'string',
+ },
+ 'code_type_equipement': {
+ 'type': 'string',
+ },
+ 'demande_lieu': {
+ 'type': 'string',
+ },
+ 'coord_x': {
+ 'type': 'string',
+ },
+ 'coord_y': {
+ 'type': 'string',
+ },
+ 'demande_priorite': {
+ 'type': 'string',
+ },
+ 'demande_objet': {
+ 'type': 'string',
+ },
+ 'demande_description': {
+ 'type': 'string',
+ },
+ 'demande_commentaire': {
+ 'type': 'string',
+ },
+ 'remote_adresse': {
+ 'type': 'string'
+ },
+ 'demande_mots_cles': {
+ 'type': 'string'
+ },
+ 'code_thematique': {
+ 'type': 'string',
+ },
+ 'code_priorite': {
+ 'type': 'string'
+ },
+ 'demande_thematique': {
+ 'type': 'string'
+ },
+ 'code_projet': {
+ 'type': 'string'
+ }
+ }
+}
+
+INSERT_ACTION_COMMENT = {
+ '$schema': 'http://json-schema.org/draft-03/schema#',
+ 'type': 'object',
+ 'properties': {
+ 'numero_demande': {
+ 'type': 'string',
+ 'required': True,
+ },
+ 'commentaire': {
+ 'type': 'string',
+ 'required': True,
+ }
+ }
+}
+
+UPLOAD = {
+ '$schema': 'http://json-schema.org/draft-03/schema#',
+ 'definitions': {
+ 'file': {
+ 'type': 'object',
+ 'properties': {
+ 'content': {
+ 'type': 'string',
+ 'required': True
+ },
+ },
+ 'required': True
+ }
+ },
+ 'type': 'object',
+ 'properties': {
+ 'file': {
+ '$ref': '#/definitions/file'
+ },
+ 'numero_demande': {
+ 'type': 'string',
+ 'required': True,
+ },
+ 'nom_fichier': {
+ 'type': 'string',
+ 'required': True,
+ }
+ }
+}
diff --git a/tests/test_atal.py b/tests/test_atal.py
index 5b049351..c88ed36f 100644
--- a/tests/test_atal.py
+++ b/tests/test_atal.py
@@ -1,3 +1,5 @@
+import base64
+
from django.contrib.contenttypes.models import ContentType
import mock
import pytest
@@ -42,7 +44,7 @@ REFS = [
def test_get_type_activite(app, connector, monkeypatch):
mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=REFS)
- response = app.get('/atal/slug-atal/get_type_activite')
+ response = app.get('/atal/slug-atal/get-type-activite')
assert response.json == {
'err': 0,
'data': [
@@ -56,7 +58,7 @@ def test_get_type_activite(app, connector, monkeypatch):
def test_get_type_de_voie(app, connector, monkeypatch):
mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=REFS)
- response = app.get('/atal/slug-atal/get_type_de_voie')
+ response = app.get('/atal/slug-atal/get-type-de-voie')
assert response.json == {
'err': 0,
'data': [
@@ -77,7 +79,7 @@ def test_get_types_equipement(app, connector, monkeypatch):
"""
mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=return_value)
- response = app.get('/atal/slug-atal/get_types_equipement')
+ response = app.get('/atal/slug-atal/get-types-equipement')
assert response.json == {
'err': 0,
'data': [
@@ -89,53 +91,57 @@ def test_get_types_equipement(app, connector, monkeypatch):
assert call_params['method'] == 'getTypesEquipement'
-def test_insert_demande_by_type(app, connector, monkeypatch):
+def test_insert_demande_complet_by_type(app, connector, monkeypatch):
mock_soap_call = mock_atal_soap_call(monkeypatch, return_value='DIT19050001')
params = {
- 'contact_nom': 'John Doe',
- 'contact_telephone': '0101010101',
- 'contact_email': 'john@doe.com',
- 'contact_adresse': '1 doe street',
- 'demande_objet': 'sarah connor',
- 'demande_lieu': 'LA',
- 'demande_description': 'poker face',
- 'remote_adresse': 'hollywood bd',
- 'code_equipement': 'MAC10',
- 'code_service_demandeur': 'skynet',
- 'date_souhaite': 'now',
- 'type_demande': 'scary'
+ 'numero_demande': 'DIT19050001',
+ 'commentaire': 'aaa'
}
- response = app.post_json('/atal/slug-atal/insert_demande_by_type', params=params)
+ response = app.post_json('/atal/slug-atal/insert-action-comment', params=params)
assert response.json == {
'err': 0,
'data': {'demande_number': 'DIT19050001'}
}
call_params = mock_soap_call.call_args.kwargs
assert call_params['wsdl'] == 'DemandeService'
- assert call_params['method'] == 'insertDemandeByType'
- assert call_params['contactNom'] == 'John Doe'
- assert call_params['contactTelephone'] == '0101010101'
- assert call_params['contactCourriel'] == 'john@doe.com'
- assert call_params['contactAdresse'] == '1 doe street'
- assert call_params['demandeObjet'] == 'sarah connor'
- assert call_params['demandeLieu'] == 'LA'
- assert call_params['demandeDescription'] == 'poker face'
- assert call_params['remoteAddress'] == 'hollywood bd'
- assert call_params['codeEquipement'] == 'MAC10'
- assert call_params['codeServiceDemandeur'] == 'skynet'
- assert call_params['dateSouhaitee'] == 'now'
- assert call_params['typeDemande'] == 'scary'
+ assert call_params['method'] == 'insertActionComment'
+ assert call_params['numeroDemande'] == 'DIT19050001'
+ assert call_params['commentaire'] == 'aaa'
-def test_retrieve_details_demande(app, connector, monkeypatch):
- mock_soap_call = mock_atal_soap_call(
- monkeypatch, return_value=dict(code='code1', libelle='elem1'))
- response = app.get('/atal/slug-atal/retrieve_details_demande/DIT19050001/')
+def test_upload(app, connector, monkeypatch):
+ mock_soap_call = mock_atal_soap_call(monkeypatch, return_value=None)
+ base64_str = 'eyJsYXN0X2NoZWNrIjoiMjAxOS0wNC0xMFQxMjowODoyOVoiL' + \
+ 'CJweXBpX3ZlcnNpb24iOiIxOS4wLjMifQ=='
+ params = {
+ 'numero_demande': 'DIT19050001',
+ 'nom_fichier': 'data.json',
+ 'file': {
+ 'content': base64_str
+ }
+ }
+ response = app.post_json('/atal/slug-atal/upload', params=params)
assert response.json == {
- 'err': 0,
- 'data': {'code': 'code1', 'libelle': 'elem1'}
+ 'err': 0
}
call_params = mock_soap_call.call_args.kwargs
- assert call_params['wsdl'] == 'DemandeService'
- assert call_params['method'] == 'retrieveDetailsDemande'
- assert call_params['demandeNumberParam'] == 'DIT19050001'
+ assert call_params['wsdl'] == 'ChargementPiecesJointesService'
+ assert call_params['method'] == 'upload'
+ assert call_params['numeroDemande'] == 'DIT19050001'
+ assert call_params['nomFichier'] == 'data.json'
+ assert call_params['donneesFichier'] == base64.b64decode(base64_str)
+
+ params = {
+ 'numero_demande': 'DIT19050001',
+ 'nom_fichier': 'data.json',
+ 'file': {
+ 'content': 'invalidbase64'
+ }
+ }
+ response = app.post_json('/atal/slug-atal/upload', params=params)
+ assert response.json == {
+ 'data': None,
+ 'err': 1,
+ 'err_class': 'passerelle.utils.jsonresponse.APIError',
+ 'err_desc': 'Invalid base64 string'
+ }