From 776c281feb2f4acab03b7f127bcca1275fe60ed9 Mon Sep 17 00:00:00 2001 From: Josue Kouka Date: Fri, 30 Mar 2018 15:13:32 +0200 Subject: [PATCH] add actesweb connector (#22395) --- passerelle/apps/actesweb/__init__.py | 0 .../apps/actesweb/migrations/0001_initial.py | 28 +++ .../apps/actesweb/migrations/__init__.py | 0 passerelle/apps/actesweb/models.py | 79 ++++++++ .../actesweb/templates/actesweb/demand.txt | 46 +++++ passerelle/settings.py | 1 + tests/data/actesweb/payload_birth.json | 50 +++++ tests/data/actesweb/payload_death.json | 37 ++++ tests/data/actesweb/payload_mariage.json | 68 +++++++ tests/test_actesweb.py | 171 ++++++++++++++++++ 10 files changed, 480 insertions(+) create mode 100644 passerelle/apps/actesweb/__init__.py create mode 100644 passerelle/apps/actesweb/migrations/0001_initial.py create mode 100644 passerelle/apps/actesweb/migrations/__init__.py create mode 100644 passerelle/apps/actesweb/models.py create mode 100644 passerelle/apps/actesweb/templates/actesweb/demand.txt create mode 100644 tests/data/actesweb/payload_birth.json create mode 100644 tests/data/actesweb/payload_death.json create mode 100644 tests/data/actesweb/payload_mariage.json create mode 100644 tests/test_actesweb.py diff --git a/passerelle/apps/actesweb/__init__.py b/passerelle/apps/actesweb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/passerelle/apps/actesweb/migrations/0001_initial.py b/passerelle/apps/actesweb/migrations/0001_initial.py new file mode 100644 index 0000000..b25ddeb --- /dev/null +++ b/passerelle/apps/actesweb/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0006_resourcestatus'), + ] + + operations = [ + migrations.CreateModel( + name='ActesWeb', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('title', models.CharField(max_length=50, verbose_name='Title')), + ('description', models.TextField(verbose_name='Description')), + ('slug', models.SlugField(unique=True)), + ('log_level', models.CharField(default=b'INFO', max_length=10, verbose_name='Log Level', choices=[(b'NOTSET', b'NOTSET'), (b'DEBUG', b'DEBUG'), (b'INFO', b'INFO'), (b'WARNING', b'WARNING'), (b'ERROR', b'ERROR'), (b'CRITICAL', b'CRITICAL')])), + ('users', models.ManyToManyField(to='base.ApiUser', blank=True)), + ], + options={ + 'verbose_name': "ActesWeb - Demande d'acte d'\xe9tat civil", + }, + ), + ] diff --git a/passerelle/apps/actesweb/migrations/__init__.py b/passerelle/apps/actesweb/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/passerelle/apps/actesweb/models.py b/passerelle/apps/actesweb/models.py new file mode 100644 index 0000000..ae08f1d --- /dev/null +++ b/passerelle/apps/actesweb/models.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2018 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 . +from __future__ import unicode_literals + +import json +import os +import tempfile + +from django.core.files.storage import default_storage +from django.template.loader import get_template +from django.utils.dateparse import parse_date +from django.utils.timezone import now +from django.utils.translation import ugettext_lazy as _ + +from passerelle.base.models import BaseResource +from passerelle.utils.api import endpoint +from passerelle.utils.jsonresponse import APIError + + +class ActesWeb(BaseResource): + category = _('Civil Status Connectors') + + class Meta: + verbose_name = u"ActesWeb - Demande d'acte d'état civil" + + @property + def basepath(self): + return os.path.join( + default_storage.path('actesweb'), self.slug) + + @endpoint(perm='can_access', methods=['post'], description=_('Create demand')) + def create(self, request, *args, **kwargs): + try: + payload = json.loads(request.body) + except (ValueError,): + raise APIError('Invalid payload format: json expected') + + if payload.get('certificate_type') not in ('NA', 'DE', 'MA'): + raise APIError("Invalid : expected ('NA', 'DE', 'MA')") + payload['event_date_start'] = parse_date(payload['event_date_start']).strftime('%Y%m%d') + template_name = 'actesweb/demand.txt' + demand_content = get_template(template_name).render(payload) + application_id = payload['application_id'] + # create tmp dir + tmp_dir = os.path.join(self.basepath, 'tmp') + if not os.path.exists(tmp_dir): + if default_storage.directory_permissions_mode: + d_umask = os.umask(0) + try: + os.makedirs(tmp_dir, mode=default_storage.directory_permissions_mode) + except OSError: + pass + finally: + os.umask(d_umask) + else: + os.makedirs(tmp_dir) + + filename = '%s.DEM' % now().strftime('%Y-%m-%d_%H-%M-%S_%f') + filepath = os.path.join(self.basepath, filename) + with tempfile.NamedTemporaryFile(dir=tmp_dir, suffix='.DEM', delete=False) as tpf: + tpf.write(demand_content) + tpf.flush() + os.fsync(tpf.file.fileno()) + os.rename(tpf.name, filepath) + demand_id = '%s_%s' % (application_id, os.path.basename(filepath)) + return {'data': {'demand_id': demand_id}} diff --git a/passerelle/apps/actesweb/templates/actesweb/demand.txt b/passerelle/apps/actesweb/templates/actesweb/demand.txt new file mode 100644 index 0000000..bb1b23d --- /dev/null +++ b/passerelle/apps/actesweb/templates/actesweb/demand.txt @@ -0,0 +1,46 @@ +CODAGE=TXT +TYPE=DEMANDE +VERSION=03 +TYPE_DEMANDEUR={{applicant_type}} +PRIORITE={{application_priority}} +DEMANDEUR_CIVILITE={{applicant_title_label}} +DEMANDEUR_NOM={{application_priority}} +DEMANDEUR_NOM_USAGE={{applicant_usual_name}} +DEMANDEUR_PRENOMS={{applicant_firstnames}} +DEMANDEUR_ADRESSE1={{applicant_address_street}} +DEMANDEUR_ADRESSE2={{applicant_address_complement}} +DEMANDEUR_VILLE={{applicant_address_city}} +DEMANDEUR_PAYS={{applicant_address_country}} +DEMANDEUR_TEL={{applicant_phone}} +DEMANDEUR_ADR={{applicant_email}} +DEMANDE_NOM={{concerned_lastname}} +DEMANDE_PRENOMS={{concerned_firstnames}} +DEMANDE_DATE_EVENEMENT={{event_date_start}} +TYPE_DEMANDE={{document_type_label}} +ACTE={{certificate_type|default:"particulier"}} +NB={{document_copies}} +LIEN={{applicant_status}} +MANDANT_NOM= +MANDANT_PRENOMS= +LIEU_EVENEMENT={{event_city}} +REF_ACTE={{document_ref|default:"inconnu"}} +PERE_NOM={{concerned_parent1_lastname}} +PERE_PRENOMS={{concerned_parent1_firstnames}} +MERE_NOM={{concerned_parent2_lastname}} +MERE_PRENOMS={{concerned_parent2_firstnames}} +CONJOINT_NOM={{partner_lastname}} +CONJOINT_PRENOMS={{partner_firstnames}} +CONJOINT_PERE_NOM={{partner_parent1_lastname}} +CONJOINT_PERE_PRENOMS={{partner_parent1_firstnames}} +CONJOINT_MERE_NOM={{partner_parent2_lastname}} +CONJOINT_MERE_PRENOMS={{partner_parent2_firstnames}} +MOTIF={{application_reason}} +DEMANDE_SEXE={{concerned_sex}} +DPERE_SEXE={{concerned_parent1_sex}} +DMERE_SEXE={{concerned_parent2_sex}} +CONJOINT_SEXE={{partner_sex}} +CPERE_SEXE={{partner_parent1_sex}} +CMERE_SEXE={{partner_parent2_sex}} +{% spaceless %} +COMMENTAIRE={{application_comment}} +{% endspaceless %} diff --git a/passerelle/settings.py b/passerelle/settings.py index 395ee00..715e747 100644 --- a/passerelle/settings.py +++ b/passerelle/settings.py @@ -115,6 +115,7 @@ INSTALLED_APPS = ( 'passerelle.base', 'passerelle.datasources', # connectors + 'passerelle.apps.actesweb', 'passerelle.apps.airquality', 'passerelle.apps.api_particulier', 'passerelle.apps.atos_genesys', diff --git a/tests/data/actesweb/payload_birth.json b/tests/data/actesweb/payload_birth.json new file mode 100644 index 0000000..40242fb --- /dev/null +++ b/tests/data/actesweb/payload_birth.json @@ -0,0 +1,50 @@ +{ + "applicant_address_city": "Nancy", + "applicant_address_complement": "Bat A", + "applicant_address_country": "France", + "applicant_address_county": "Meurthe-et-Moselle", + "applicant_address_street": "37 Rue du Cheval Blanc", + "applicant_address_zipcode": "54000", + "applicant_email": "chelsea@whatever.com", + "applicant_firstnames": "Kim Chelsea", + "applicant_lastname": "Whatever", + "applicant_name_usage": "nom d'epouse", + "applicant_phone": "+33 6 55 44 22 11", + "applicant_status": "concerne", + "applicant_title": "Mme", + "applicant_title_label": "Madame", + "applicant_usual_name": "Whatever", + "application_id": "N201610154", + "application_origin": "internet", + "application_reason": "They are just messy", + "application_time": "2016-10-20T14:41:20Z", + "certificate_type": "NA", + "certificate_type_label": "Acte de naissance", + "concerned_birth_city": "Harare", + "concerned_birth_country": "Zimbabwe", + "concerned_birth_county": "", + "concerned_birth_date": "1980-02-29", + "concerned_firstnames": "Kevin", + "concerned_lastname": "Whatever", + "concerned_name_usage": "", + "concerned_parent1_firstnames": "John Oliver", + "concerned_parent1_lastname": "Smith", + "concerned_parent1_name_usage": "Smith", + "concerned_parent1_title": "M", + "concerned_parent1_title_label": "Monsieur", + "concerned_parent1_usual_name": "Smith", + "concerned_parent2_firstnames": "Kim", + "concerned_parent2_lastname": "Smith", + "concerned_parent2_name_usage": "nom d'\u00e9pouse", + "concerned_parent2_title": "Mme", + "concerned_parent2_usual_name": "Smith", + "concerned_sex": "m", + "concerned_title": "M", + "concerned_usual_name": "Whatever", + "document_copies": "1", + "document_type": "CPI", + "document_type_label": "Copie Integrale", + "event_city": "Nancy", + "event_date_end": "", + "event_date_start": "2012-07-14" +} diff --git a/tests/data/actesweb/payload_death.json b/tests/data/actesweb/payload_death.json new file mode 100644 index 0000000..a530c6b --- /dev/null +++ b/tests/data/actesweb/payload_death.json @@ -0,0 +1,37 @@ +{ + "applicant_address_city": "Nancy", + "applicant_address_complement": "Bat A", + "applicant_address_country": "France", + "applicant_address_county": "Meurthe-et-Moselle", + "applicant_email": "chelsea@whatever.com", + "applicant_phone": "+33 6 55 44 22 11", + "applicant_address_street": "37 Rue du Cheval Blanc", + "applicant_address_zipcode": "54000", + "applicant_firstnames": "Kim Chelsea", + "applicant_lastname": "Whatever", + "applicant_name_usage": "nom d'epouse", + "applicant_status": "concerne", + "applicant_title": "Mme", + "applicant_title_label": "Madame", + "applicant_usual_name": "Whatever", + "application_id": "D201610171", + "application_origin": "internet", + "application_reason": "", + "application_time": "2016-10-20T14:41:20Z", + "certificate_type": "DE", + "certificate_type_label": "Acte de d\u00e9c\u00e8s", + "concerned_birth_city": "Harare", + "concerned_birth_country": "Zimbabwe", + "concerned_birth_county": "", + "concerned_birth_date": "1980-02-29", + "concerned_firstnames": "Kevin", + "concerned_lastname": "Whatever", + "concerned_sex": "m", + "concerned_title": "M", + "concerned_usual_name": "Whatever", + "document_copies": "1", + "document_type": "EXTSF", + "document_type_label": "Extrait sans filiation", + "event_city": "Nancy", + "event_date_start": "2012-07-14" +} diff --git a/tests/data/actesweb/payload_mariage.json b/tests/data/actesweb/payload_mariage.json new file mode 100644 index 0000000..2fe6943 --- /dev/null +++ b/tests/data/actesweb/payload_mariage.json @@ -0,0 +1,68 @@ +{ + "applicant_address_city": "Nancy", + "applicant_address_complement": "Bat A", + "applicant_address_country": "France", + "applicant_address_county": "Meurthe-et-Moselle", + "applicant_email": "chelsea@whatever.com", + "applicant_phone": "+33 6 55 44 22 11", + "applicant_address_street": "37 Rue du Cheval Blanc", + "applicant_address_zipcode": "54000", + "applicant_firstnames": "Kim Chelsea", + "applicant_lastname": "Whatever", + "applicant_name_usage": "nom d'epouse", + "applicant_status": "concerne", + "applicant_title": "Mme", + "applicant_title_label": "Madame", + "applicant_usual_name": "Whatever", + "application_id": "M201610161", + "application_origin": "internet", + "application_reason": "Happy mariage", + "application_time": "2016-10-20T14:41:20Z", + "certificate_type": "MA", + "certificate_type_label": "Acte de naissance", + "concerned_birth_city": "Harare", + "concerned_birth_country": "Zimbabwe", + "concerned_birth_county": "", + "concerned_birth_date": "1980-02-29", + "concerned_firstnames": "Kevin", + "concerned_lastname": "Whatever", + "concerned_name_usage": "", + "concerned_parent1_firstnames": "John Oliver", + "concerned_parent1_lastname": "Smith", + "concerned_parent1_name_usage": "Smith", + "concerned_parent1_title": "M", + "concerned_parent1_title_label": "Monsieur", + "concerned_parent1_usual_name": "Smith", + "concerned_parent2_firstnames": "Kim", + "concerned_parent2_lastname": "Smith", + "concerned_parent2_name_usage": "nom d'\u00e9pouse", + "concerned_parent2_title": "Mme", + "concerned_parent2_usual_name": "Smith", + "concerned_sex": "m", + "concerned_title": "M", + "document_copies": "1", + "document_type": "CPI", + "document_type_label": "Copie Integrale", + "event_city": "Nancy", + "event_date_end": "", + "event_date_start": "2012-07-14", + "partner_birth_city": "Harare", + "partner_birth_country": "Zimbabwe", + "partner_birth_county": "", + "partner_birth_date": "1984-02-29", + "partner_firstnames": "Chelsea", + "partner_lastname": "Contrao", + "partner_name_usage": "", + "partner_parent1_firstnames": "Antonio", + "partner_parent1_lastname": "Scaramucci", + "partner_parent1_title": "M", + "partner_parent1_title_label": "Monsieur", + "partner_parent2_firstnames": "Marguerite", + "partner_parent2_lastname": "Scaramucci", + "partner_parent2_name_usage": "nom d'\u00e9pouse", + "partner_parent2_title": "Mme", + "partner_parent2_usual_name": "Gaye", + "partner_sex": "F", + "partner_title": "Mme", + "partner_usual_name": "Scaramucci" +} diff --git a/tests/test_actesweb.py b/tests/test_actesweb.py new file mode 100644 index 0000000..c055014 --- /dev/null +++ b/tests/test_actesweb.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Passerelle - uniform access to data and services +# Copyright (C) 2018 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; exclude 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.deepcopy of the GNU Affero General Public License +# along with this program. If not, see . +from __future__ import unicode_literals + +import json +import io +import os +import shutil + +import pytest + +import utils +from passerelle.apps.actesweb.models import ActesWeb + + +def get_test_base_dir(name): + return os.path.join(os.path.dirname(__file__), 'data', name) + + +def get_file_from_test_base_dir(filename): + path = os.path.join(get_test_base_dir('actesweb'), filename) + with open(path, 'rb') as fd: + return fd.read() + + +@pytest.fixture +def actesweb(db): + return utils.make_resource(ActesWeb, **{'slug': 'test'}) + + +@pytest.fixture(autouse=True) +def media_dir(tmpdir, settings): + tmp_dir = tmpdir.mkdir('actesweb').dirname + settings.MEDIA_ROOT = tmp_dir + yield tmp_dir + shutil.rmtree(tmp_dir, ignore_errors=True) + + +PAYLOAD = [ + { + 'birth': json.loads(get_file_from_test_base_dir('payload_birth.json')) + }, + { + 'mariage': json.loads(get_file_from_test_base_dir('payload_mariage.json')) + }, + { + 'death': json.loads(get_file_from_test_base_dir('payload_death.json')) + } +] + + +@pytest.fixture(params=PAYLOAD) +def payload(request): + return request.param + + +def get_demand_filepath(con, demand_id): + filename = '_'.join(demand_id.split('_')[1:]) + return os.path.join(con.basepath, filename) + + +def assert_file_content_values(filename, expectations): + with io.open(filename, 'rb') as fp: + for line in fp.readlines(): + field, value = line.split('=') + if field in expectations: + assert value.strip() == expectations[field] + + +def test_demand_creation(app, payload, actesweb): + url = '/actesweb/test/create/' + if 'birth' in payload: + response = app.post_json(url, params=payload['birth']) + demand_id = response.json['data']['demand_id'] + demfile = get_demand_filepath(actesweb, demand_id) + assert_file_content_values( + demfile, dict( + DEMANDEUR_CIVILITE="Madame", + DEMANDEUR_NOM_USAGE="Whatever", + DEMANDEUR_PRENOMS="Kim Chelsea", + DEMANDEUR_ADRESSE1="37 Rue du Cheval Blanc", + DEMANDEUR_VILLE="Nancy", + DEMANDEUR_PAYS="France", + DEMANDEUR_TEL="+33 6 55 44 22 11", + DEMANDEUR_ADR="chelsea@whatever.com", + DEMANDE_NOM="Whatever", + DEMANDE_PRENOMS="Kevin", + DEMANDE_DATE_EVENEMENT="20120714", + TYPE_DEMANDE="Copie Integrale", + ACTE="NA", + NB="1", + LIEU_EVENEMENT="Nancy", + PERE_NOM="Smith", + PERE_PRENOMS="John Oliver", + MERE_NOM="Smith", + MERE_PRENOM="Kim", + DEMANDE_SEXE="m" + ) + ) + elif 'mariage' in payload: + response = app.post_json(url, params=payload['mariage']) + demand_id = response.json['data']['demand_id'] + demfile = get_demand_filepath(actesweb, demand_id) + assert_file_content_values( + demfile, dict( + DEMANDEUR_CIVILITE="Madame", + DEMANDEUR_NOM_USAGE="Whatever", + DEMANDEUR_PRENOMS="Kim Chelsea", + DEMANDEUR_ADRESSE1="37 Rue du Cheval Blanc", + DEMANDEUR_VILLE="Nancy", + DEMANDEUR_PAYS="France", + DEMANDEUR_TEL="+33 6 55 44 22 11", + DEMANDEUR_ADR="chelsea@whatever.com", + DEMANDE_NOM="Whatever", + DEMANDE_PRENOMS="Kevin", + DEMANDE_DATE_EVENEMENT="20120714", + TYPE_DEMANDE="Copie Integrale", + ACTE="MA", + NB="1", + LIEU_EVENEMENT="Nancy", + PERE_NOM="Smith", + PERE_PRENOMS="John Oliver", + MERE_NOM="Smith", + MERE_PRENOM="Kim", + DEMANDE_SEXE="m", + CONJOINT_NOM="Contrao", + CONJOIN_PRENOMS="Chelsea", + CONJOINT_PERE_NOM="Scaramucci", + CONJOINT_PERE_PRENOMS="Antonio", + CONJOINT_MERE_NOM="Scaramucci", + CONJOINT_MERE_PRENOMS="Marguerite", + ) + ) + else: + response = app.post_json(url, params=payload['death']) + demand_id = response.json['data']['demand_id'] + demfile = get_demand_filepath(actesweb, demand_id) + assert_file_content_values( + demfile, dict( + DEMANDEUR_CIVILITE="Madame", + DEMANDEUR_NOM_USAGE="Whatever", + DEMANDEUR_PRENOMS="Kim Chelsea", + DEMANDEUR_ADRESSE1="37 Rue du Cheval Blanc", + DEMANDEUR_VILLE="Nancy", + DEMANDEUR_PAYS="France", + DEMANDEUR_TEL="+33 6 55 44 22 11", + DEMANDEUR_ADR="chelsea@whatever.com", + DEMANDE_NOM="Whatever", + DEMANDE_PRENOMS="Kevin", + DEMANDE_DATE_EVENEMENT="20120714", + TYPE_DEMANDE="Extrait sans filiation", + ACTE="DE", + NB="1", + LIEU_EVENEMENT="Nancy", + DEMANDE_SEXE="m" + ) + ) -- 2.19.1