cityweb: add cityweb connector (#15883)

This commit is contained in:
Josue Kouka 2017-05-23 00:40:18 +02:00
parent 9831bf4fe3
commit 002e266bae
14 changed files with 1027 additions and 0 deletions

1
debian/control vendored
View File

@ -24,6 +24,7 @@ Depends: ${python:Depends},
python-magic,
python-suds,
python-cmislib (>= 0.5), python-cmislib (< 0.6),
python-lxml
Recommends: python-soappy, python-phpserialize
Suggests: python-sqlalchemy, python-mako
Description: Uniform access to multiple data sources and services (Python module)

View File

View File

@ -0,0 +1,386 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 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 os
import zipfile
from dateutil.parser import parse as dateutil_parse
from lxml import etree, objectify as xobject
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from passerelle.utils.jsonresponse import APIError
CERTIFICATE_TYPES = [
{"id": "NAI", "text": "Naissance"},
{"id": "MAR", "text": "Mariage"},
{"id": "REC", "text": "Reconnaissance"},
{"id": "DEC", "text": "Décès"}
]
SEXES = [
{"id": "M", "text": "Homme"},
{"id": "F", "text": "Femme"},
{"id": "NA", "text": "Autre"}
]
TITLES = [
{"id": "M", "text": "Monsieur"},
{"id": "Mme", "text": "Madame"},
{"id": "Mlle", "text": "Mademoiselle"}
]
DOCUMENT_TYPES = [
{"id": "CPI", "text": "Copie intégrale"},
{"id": "EXTAF", "text": "Extrait avec filiation"},
{"id": "EXTSF", "text": "Extrait sans filiation"},
{"id": "EXTPL", "text": "Extrait plurilingue"}
]
CONCERNED = [
{"id": "reconnu", "text": "Reconnu"},
{"id": "auteur", "text": "Auteur"}
]
ORIGINS = [
{"id": "internet", "text": "Internet"},
{"id": "guichet", "text": "Guichet"},
{"id": "courrier", "text": "Courrier"}
]
def is_clean(element):
if not element.getchildren() and element.text is None:
return False
return all(is_clean(child) for child in element.iterchildren())
class BaseType(object):
"""Base data binding object
"""
tagname = None
def __repr__(self):
return '<%s>' % self.tagname
@classmethod
def make_element(cls, tagname, value=None, namespace=None, nsmap=None):
M = xobject.ElementMaker(annotate=False, namespace=namespace,
nsmap=nsmap)
return M(tagname, value)
@property
def xml(self):
if isinstance(self, (SimpleType, DateType)):
return self.make_element(self.tagname, self.value, namespace=self.namespace)
tag = self.make_element(self.tagname, namespace=self.namespace, nsmap=self.nsmap)
for subelt in self.sequence:
attr = getattr(self, subelt, None)
if not attr:
continue
if isinstance(attr, (str, unicode)):
tag.append(self.make_element(subelt, attr, namespace=self.namespace))
else:
xml = attr.xml
if not is_clean(xml):
continue
tag.append(xml)
xobject.deannotate(tag, xsi_nil=True)
return tag
def __str__(self):
return etree.tostring(self.xml, pretty_print=True)
class CityWebType(BaseType):
namespace = 'http://tempuri.org/XMLSchema.xsd'
nsmap = {'xs': 'http://tempuri.org/XMLSchema.xsd'}
class SimpleType(CityWebType):
"""Data binding class for SimpleType
"""
allowed_values = None
def __init__(self, value):
if value not in self.allowed_values:
raise APIError('<%s> value (%s) not in %s' % (self.tagname, value,
self.allowed_values))
self.value = value
class DateType(CityWebType):
def __init__(self, value):
try:
self.value = dateutil_parse(value).date().isoformat()
except (ValueError,) as exc:
raise APIError(exc.message)
class ComplexType(CityWebType):
"""Data binding class for ComplexType
"""
sequence = None
pattern = None
def __init__(self, data):
if self.pattern:
data = self.extract_by_pattern(data)
self.data = data
def extract_by_pattern(self, data):
data = {key: value for key, value in data.iteritems() if self.pattern in key}
data = {key.replace(self.pattern, ''): value for key, value in data.iteritems()}
return data
class BirthDate(DateType):
tagname = 'date'
class StartDate(DateType):
tagname = 'dateDebut'
class EndDate(DateType):
tagname = 'dateFin'
# SIMPLE TYPES
class Document(SimpleType):
tagname = 'natureDocument'
allowed_values = map(lambda x: x['id'], DOCUMENT_TYPES)
class Origin(SimpleType):
tagname = 'origine'
allowed_values = map(lambda x: x['id'], ORIGINS)
class Title(SimpleType):
tagname = 'genre'
allowed_values = map(lambda x: x['id'], TITLES)
class Sex(SimpleType):
tagname = 'sexe'
allowed_values = map(lambda x: x['id'], SEXES)
class Certificate(SimpleType):
tagname = 'natureEvenement'
allowed_values = map(lambda x: x['id'], CERTIFICATE_TYPES)
class ConcernedKind(SimpleType):
tagname = 'typeInteresse'
allowed_values = ('reconnu', 'auteur')
# COMPLEX TYPES
class Names(ComplexType):
tagname = 'noms'
sequence = ('nomDeFamille', 'nomUsgae', 'typeUsage')
def __init__(self, data):
super(Names, self).__init__(data)
if self.data.get('lastname'):
self.nomDeFamille = self.data['lastname']
self.nomUsage = self.data.get('usual_name', '')
self.typeUsage = self.data.get('name_usage', '')
class Place(ComplexType):
tagname = 'lieu'
sequence = ('ville', 'province', 'pays')
def __init__(self, data):
super(Place, self).__init__(data)
if self.data.get('city'):
self.ville = self.data['city']
self.province = self.data.get('county', '')
self.pays = self.data.get('country', '')
class Address(ComplexType):
tagname = 'adresse'
sequence = ('ligneAdr1', 'ligneAdr2', 'codePostal',
'lieu', 'mail', 'tel')
pattern = 'address_'
def __init__(self, data):
super(Address, self).__init__(data)
self.ligneAdr1 = self.data.get('street', '')
self.ligneAdr2 = self.data.get('complement', '')
self.lieu = Place(self.data)
self.mail = self.data.get('email', '')
self.tel = self.data.get('phone', '')
class Birth(ComplexType):
tagname = 'naissance'
sequence = ('date', 'lieu')
pattern = 'birth_'
def __init__(self, data):
super(Birth, self).__init__(data)
birthdate = self.data.get('date', None)
if birthdate:
self.date = BirthDate(birthdate)
self.lieu = Place(self.data)
class EventDate(ComplexType):
tagname = 'dateEvenement'
sequence = ('dateDebut', 'dateFin')
def __init__(self, data):
super(EventDate, self).__init__(data)
self.dateDebut = StartDate(self.data['event_date_start'])
if data.get('event_date_end', None):
self.dateFin = EndDate(self.data['event_date_end'])
class EventPlace(Place):
tagname = 'lieuEvenement'
pattern = 'event_'
class Person(ComplexType):
sequence = ('noms', 'prenoms', 'genre', 'adresse', 'sexe',
'pere', 'mere', 'naissance')
def __init__(self, data):
super(Person, self).__init__(data)
self.noms = Names(self.data)
self.prenoms = self.data.get('firstnames', '')
if self.data.get('title', None):
self.genre = Title(self.data['title'])
self.adresse = Address(self.data)
if self.data.get('sex', None):
self.sexe = Sex(self.data['sex'])
self.naissance = Birth(self.data)
class ApplicantPerson(Person):
tagname = 'individu'
pattern = 'applicant_'
sequence = ('noms', 'prenoms', 'genre', 'adresse')
class Parent(Person):
sequence = ('noms', 'prenoms', 'genre')
def __init__(self, data, pattern):
self.pattern = pattern
super(Parent, self).__init__(data)
sex = self.data.get('sex')
if sex:
if sex == 'M':
self.tagname = 'pere'
else:
self.tagname = 'mere'
else:
if pattern.startswith('parent1'):
self.tagname = 'pere'
else:
self.tagname = 'mere'
class ConcernedCommon(Person):
sequence = ('noms', 'prenoms', 'genre', 'sexe',
'parent1', 'parent2', 'naissance')
def __init__(self, data):
super(ConcernedCommon, self).__init__(data)
self.parent1 = Parent(self.data, 'parent1_')
self.parent2 = Parent(self.data, 'parent2_')
class Concerned(ConcernedCommon):
tagname = 'interesse'
pattern = 'concerned_'
class Partner(ConcernedCommon):
tagname = 'conjoint'
pattern = 'partner_'
class Applicant(ComplexType):
tagname = 'demandeur'
sequence = ('qualiteDemandeur', 'individu')
def __init__(self, data):
self.qualiteDemandeur = data.get('applicant_status', '')
self.individu = ApplicantPerson(data)
class Event(ComplexType):
tagname = 'evenement'
sequence = ('interesse', 'conjoint', 'natureEvenement',
'typeInteresse', 'dateEvenement', 'lieuEvenement')
def __init__(self, data):
certificate_type = data['certificate_type']
self.interesse = Concerned(data)
if certificate_type == 'MAR':
self.conjoint = Partner(data)
self.natureEvenement = Certificate(data['certificate_type'])
if data.get('concerned_kind', None):
self.typeInteresse = ConcernedKind(data['concerned_kind'])
self.dateEvenement = EventDate(data)
self.lieuEvenement = EventPlace(data)
class CivilStatusApplication(ComplexType):
tagname = 'demandeEtatCivil'
sequence = (
'identifiant', 'demandeur', 'natureDocument', 'nbExemplaire',
'dateDemande', 'evenement', 'motif', 'origine', 'commentaire')
def __init__(self, data):
self.identifiant = data['application_id']
self.demandeur = Applicant(data)
self.natureDocument = Document(data['document_type'])
self.nbExemplaire = data['document_copies']
self.dateDemande = data['application_time']
self.evenement = Event(data)
self.motif = data.get('application_reason', '')
if data.get('application_origin', None):
self.origine = Origin(data['application_origin'])
self.commentaire = data.get('application_comment', '')
@property
def demand_id(self):
return '%s-%s' % (self.identifiant, self.evenement.natureEvenement.value)
def save(self, path):
basename = os.path.join(path, self.demand_id)
archname = basename + '.zip'
filepath = basename + '.xml'
content = etree.tostring(self.xml, pretty_print=True)
default_storage.save(filepath, ContentFile(content))
with zipfile.ZipFile(archname, 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(filepath, os.path.basename(filepath))
os.remove(filepath)
return archname

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0005_resourcelog'),
]
operations = [
migrations.CreateModel(
name='CityWeb',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('title', models.CharField(max_length=50)),
('slug', models.SlugField()),
('description', models.TextField()),
('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'), (b'FATAL', b'FATAL')])),
('users', models.ManyToManyField(to='base.ApiUser', blank=True)),
],
options={
'verbose_name': "CityWeb - Demande d'acte d'\xe9tat civil",
},
)
]

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017 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 os
import json
from django.utils.translation import ugettext_lazy as _
from django.core.files.storage import default_storage
from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
from .cityweb import (CivilStatusApplication, TITLES, SEXES, DOCUMENT_TYPES,
CERTIFICATE_TYPES, CONCERNED, ORIGINS)
class CityWeb(BaseResource):
category = _('Business Process Connectors')
class Meta:
verbose_name = "CityWeb - Demande d'acte d'état civil"
@classmethod
def get_verbose_name(cls):
return cls._meta.verbose_name
@endpoint(perm='can_access', methods=['post'], description=_('Create a demand'))
def create(self, request, *args, **kwargs):
payload = json.loads(request.body)
# check mandatory keys
for key in ('application_id', 'application_time', 'certificate_type'):
if key not in payload:
raise APIError('<%s> is required' % key)
application = CivilStatusApplication(payload)
application.save(self.basepath)
return {'data': {'demand_id': application.demand_id}}
@property
def basepath(self):
return os.path.join(
default_storage.path('cityweb'), self.slug)
@endpoint(perm='can_access', description=_('Get title list'))
def titles(self, request):
return {'data': TITLES}
@endpoint(perm='can_access', description=_('Get sex list'))
def sexes(self, request):
return {'data': SEXES}
@endpoint(perm='can_access', description=_('Get concerned status list'))
def concerned(self, request):
return {'data': CONCERNED}
@endpoint(perm='can_access', description=_('Get application origin list'))
def origins(self, request):
return {'data': ORIGINS}
@endpoint(name='certificate-types', perm='can_access',
description=_('Get certificate type list'), parameters={'exclude': {'example_value': 'REC'}})
def certificate_types(self, request, exclude=''):
return {'data': [item for item in CERTIFICATE_TYPES
if item.get('id') not in exclude.split(',')]}
@endpoint(name='document-types', perm='can_access',
description=_('Get document type list'), parameters={'exclude': {'example_value': 'EXTPL'}})
def document_types(self, request, exclude=''):
return {'data': [item for item in DOCUMENT_TYPES
if item.get('id') not in exclude.split(',')]}

View File

@ -116,6 +116,7 @@ INSTALLED_APPS = (
'passerelle.apps.airquality',
'passerelle.apps.okina',
'passerelle.apps.cmis',
'passerelle.apps.cityweb',
# backoffice templates and static
'gadjo',
)

View File

@ -98,6 +98,7 @@ setup(name='passerelle',
'pyexcel-xls >= 0.4, <0.5',
'cmislib >= 0.5, <0.6',
'feedparser',
'lxml'
],
cmdclass={
'build': build,

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- edited with XMLSpy v2005 U (http://www.xmlspy.com) by judlin (digitech) -->
<xs:schema xmlns="http://tempuri.org/XMLSchema.xsd" xmlns:mstns="http://tempuri.org/XMLSchema.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://tempuri.org/XMLSchema.xsd" elementFormDefault="qualified">
<xs:element name="demandeEtatCivil">
<xs:annotation>
<xs:documentation>Root element</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="identifiant" type="xs:string" />
<xs:element name="demandeur" type="demandeurType" minOccurs="0">
<xs:annotation>
<xs:documentation>utile si différent de l'interessé</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="natureDocument" type="natureDocumentType">
<xs:annotation>
<xs:documentation>copie intégrale, extrait avec ou sans filiation, plurilingue</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="nbExemplaire" type="xs:int" minOccurs="0" />
<xs:element name="dateDemande" type="xs:dateTime" />
<xs:element name="evenement" type="evenementType" />
<xs:element name="motif" type="xs:string" minOccurs="0" />
<xs:element name="origine" type="origineECType" minOccurs="0">
<xs:annotation>
<xs:documentation>origine de la demande pour l'état civil : courrier, guichet ou internet</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="commentaire" type="xs:string" minOccurs="0" >
<xs:annotation>
<xs:documentation>champ commentaire</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute name="version" type="xs:string" default="1.0"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="natureEvenementType">
<xs:annotation>
<xs:documentation>Naissance, mariage, décès, reconnaissance</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="NAI"/>
<xs:enumeration value="DEC"/>
<xs:enumeration value="MAR"/>
<xs:enumeration value="REC"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="individuType">
<xs:sequence>
<xs:element name="noms" type="nomsType"/>
<xs:element name="prenoms" type="xs:string" minOccurs="0"/>
<xs:element name="genre" type="genreType" minOccurs="0"/>
<xs:element name="adresse" type="adresseType" minOccurs="0"/>
<xs:element name="sexe" type="sexeType" minOccurs="0"/>
<xs:element name="pere" type="individuType" minOccurs="0"/>
<xs:element name="mere" type="individuType" minOccurs="0"/>
<xs:element name="naissance" type="naissanceType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="demandeurType">
<xs:annotation>
<xs:documentation>Informations sur le demandeur</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="qualiteDemandeur" type="xs:string">
<xs:annotation>
<xs:documentation>avocat, notaire, père, mère...</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="individu" type="individuType"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="sexeType">
<xs:annotation>
<xs:documentation>permet de gérer le sexe indeterminé</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="F"/>
<xs:enumeration value="M"/>
<xs:enumeration value="NA"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="genreType">
<xs:restriction base="xs:string">
<xs:enumeration value="M"/>
<xs:enumeration value="Mme"/>
<xs:enumeration value="Mlle"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="adresseType">
<xs:sequence>
<xs:element name="ligneAdr1" type="xs:string" minOccurs="0"/>
<xs:element name="ligneAdr2" type="xs:string" minOccurs="0"/>
<xs:element name="codePostal" type="xs:string" minOccurs="0"/>
<xs:element name="lieu" type="lieuType"/>
<xs:element name="mail" type="xs:string" minOccurs="0"/>
<xs:element name="tel" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="typeInteresseType">
<xs:restriction base="xs:string">
<xs:enumeration value="reconnu"/>
<xs:enumeration value="auteur"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="natureDocumentType">
<xs:restriction base="xs:string">
<xs:enumeration value="CPI"/>
<xs:enumeration value="EXTAF"/>
<xs:enumeration value="EXTSF"/>
<xs:enumeration value="EXTPL"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="origineECType">
<xs:restriction base="xs:string">
<xs:enumeration value="internet"/>
<xs:enumeration value="guichet"/>
<xs:enumeration value="courrier"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="evenementType">
<xs:sequence>
<xs:element name="interesse" type="individuType"/>
<xs:element name="conjoint" type="individuType" minOccurs="0">
<xs:annotation>
<xs:documentation>Seulement pour les mariages</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="natureEvenement" type="natureEvenementType">
<xs:annotation>
<xs:documentation>naissance, mariage, décès, reconnaissance</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="typeInteresse" type="typeInteresseType" minOccurs="0">
<xs:annotation>
<xs:documentation>necessaire pour les reconnaissances seulement, l'interessé pouvant etre le parent ou le reconnu, il faut donc préciser.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="dateEvenement" type="dateEvenementType"/>
<xs:element name="lieuEvenement" type="lieuType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="nomsType">
<xs:sequence>
<xs:element name="nomDeFamille" type="xs:string"/>
<xs:element name="nomUsage" type="xs:string" minOccurs="0"/>
<xs:element name="typeUsage" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>précission sur le nom d'usage: nom d'épouse, veuve, d'usage...</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="naissanceType">
<xs:sequence>
<xs:element name="date" type="dateCiviqueType"/>
<xs:element name="lieu" type="lieuType" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="lieuType">
<xs:sequence>
<xs:element name="ville" type="xs:string"/>
<xs:element name="province" type="xs:string" minOccurs="0"/>
<xs:element name="pays" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="dateEvenementType">
<xs:sequence>
<xs:element name="dateDebut" type="xs:date"/>
<xs:element name="dateFin" type="xs:date" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="dateCiviqueType">
<xs:annotation>
<xs:documentation>Permet de gérer les dates incomplètes</xs:documentation>
</xs:annotation>
<xs:union memberTypes="xs:date xs:gYear xs:gYearMonth"/>
</xs:simpleType>
</xs:schema>

View File

@ -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_email": "chelsea@whatever.com",
"applicant_address_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": "15-4",
"application_origin": "internet",
"application_reason": "They are just messy",
"application_time": "2016-10-20T14:41:20Z",
"certificate_type": "NAI",
"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"
}

View File

@ -0,0 +1,37 @@
{
"applicant_address_city": "Nancy",
"applicant_address_complement": "Bat A",
"applicant_address_country": "France",
"applicant_address_county": "Meurthe-et-Moselle",
"applicant_address_email": "chelsea@whatever.com",
"applicant_address_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_origin": "internet",
"application_id": "17-1",
"application_reason": "",
"application_time": "2016-10-20T14:41:20Z",
"certificate_type": "DEC",
"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"
}

View File

@ -0,0 +1,68 @@
{
"applicant_address_city": "Nancy",
"applicant_address_complement": "Bat A",
"applicant_address_country": "France",
"applicant_address_county": "Meurthe-et-Moselle",
"applicant_address_email": "chelsea@whatever.com",
"applicant_address_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": "16-1",
"application_origin": "internet",
"application_reason": "Happy mariage",
"application_time": "2016-10-20T14:41:20Z",
"certificate_type": "MAR",
"certificate_type_label": "Acte de naissance",
"concerned_birth_city": "Harare",
"concerned_birth_country": "Zimbabwe",
"concerned_birth_county": "",
"concerned_birth_date": "1980-02-29",
"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_firstnames": "Kevin",
"concerned_lastname": "Whatever",
"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_name_usage": "",
"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_parent1_firstnames": "Antonio",
"partner_parent1_lastname": "Scaramucci",
"partner_parent1_title": "M",
"partner_parent1_title_label": "Monsieur",
"partner_firstnames": "Chelsea",
"partner_lastname": "Contrao",
"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_name_usage": "",
"partner_sex": "F",
"partner_title": "Mme",
"partner_usual_name": "Scaramucci"
}

189
tests/test_cityweb.py Normal file
View File

@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
# Passerelle - uniform access to data and services
# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
import os
import json
import shutil
import pytest
import mock
from lxml import etree, objectify as xobject
import zipfile
import utils
from passerelle.apps.cityweb.models import CityWeb
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('cityweb'), filename)
with open(path, 'rb') as fd:
return fd.read()
@pytest.fixture
def setup(db):
return utils.setup_access_rights(CityWeb.objects.create(slug='test'))
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 assert_xml_doc(archive):
with zipfile.ZipFile(archive) as zfd:
data = zfd.read(zfd.filelist[0].filename)
schema = etree.XMLSchema(
etree.parse(os.path.join(get_test_base_dir('cityweb'), 'cityweb.xsd')))
schema.assertValid(etree.fromstring(data))
@mock.patch('passerelle.apps.cityweb.models.default_storage.path', get_test_base_dir)
def test_demand_creation(app, setup, payload):
url = '/cityweb/test/create/'
if 'birth' in payload:
response = app.post_json(url, params=payload['birth'])
assert response.json['data']['demand_id'] == '15-4-NAI'
archive = os.path.join(get_test_base_dir('cityweb'), 'test', '15-4-NAI.zip')
assert_xml_doc(archive)
elif 'mariage' in payload:
response = app.post_json(url, params=payload['mariage'])
assert response.json['data']['demand_id'] == '16-1-MAR'
archive = os.path.join(get_test_base_dir('cityweb'), 'test', '16-1-MAR.zip')
assert_xml_doc(archive)
else:
response = app.post_json(url, params=payload['death'])
assert response.json['data']['demand_id'] == '17-1-DEC'
archive = os.path.join(get_test_base_dir('cityweb'), 'test', '17-1-DEC.zip')
assert_xml_doc(archive)
def test_datasource_titles(app, setup):
response = app.get('/cityweb/test/titles/')
data = response.json['data']
assert len(data) == 3
for datum in data:
if datum['id'] == 'M':
assert datum['text'] == 'Monsieur'
elif datum['id'] == 'Mme':
assert datum['text'] == 'Madame'
else:
assert datum['id'] == 'Mlle'
assert datum['text'] == 'Mademoiselle'
def test_datasource_sexes(app, setup):
response = app.get('/cityweb/test/sexes/')
data = response.json['data']
assert len(data) == 3
for datum in data:
if datum['id'] == 'M':
assert datum['text']
elif datum['id'] == 'F':
assert datum['text'] == 'Femme'
else:
assert datum['id'] == 'NA'
assert datum['text'] == 'Autre'
def test_datasource_concerned(app, setup):
response = app.get('/cityweb/test/concerned/')
data = response.json['data']
assert len(data) == 2
for datum in data:
if datum['id'] == 'reconnu':
assert datum['text'] == 'Reconnu'
else:
assert datum['id'] == 'auteur'
assert datum['text'] == 'Auteur'
def test_datasource_origins(app, setup):
response = app.get('/cityweb/test/origins/')
data = response.json['data']
assert len(data) == 3
for datum in data:
if datum['id'] == 'internet':
assert datum['text'] == 'Internet'
elif datum['id'] == 'guichet':
assert datum['text'] == 'Guichet'
else:
assert datum['id'] == 'courrier'
assert datum['text'] == 'Courrier'
def test_datasource_document_types(app, setup):
response = app.get('/cityweb/test/document-types/')
data = response.json['data']
assert len(data) == 4
for datum in data:
if datum['id'] == 'CPI':
assert datum['text'] == 'Copie intégrale'
elif datum['id'] == 'EXTAF':
assert datum['text'] == 'Extrait avec filiation'
elif datum['id'] == 'EXTSF':
assert datum['text'] == 'Extrait sans filiation'
else:
datum['id'] == 'EXTPL'
datum['text'] == 'Extrait plurilingue'
params = {'exclude': 'EXTAF,EXTSF,EXTPL'}
response = app.get('/cityweb/test/document-types/', params=params)
data = response.json['data']
assert len(data) == 1
assert data[0]['id'] == 'CPI'
assert data[0]['text'] == 'Copie intégrale'
def test_datasource_certificate_types(app, setup):
response = app.get('/cityweb/test/certificate-types/')
data = response.json['data']
assert len(data) == 4
for datum in data:
if datum['id'] == 'NAI':
assert datum['text'] == 'Naissance'
elif datum['id'] == 'MAR':
assert datum['text'] == 'Mariage'
elif datum['id'] == 'REC':
assert datum['text'] == 'Reconnaissance'
else:
assert datum['id'] == 'DEC'
assert datum['text'] == 'Décès'
def teardown_module(module):
shutil.rmtree(os.path.join(get_test_base_dir('cityweb'), 'test'), ignore_errors=True)

View File

@ -23,6 +23,7 @@ deps =
pylint
pylint-django
django-webtest
lxml
commands =
./getmagic.sh
py.test {env:FAST:} {env:COVERAGE:} {posargs:tests/}