cityweb: add cityweb connector (#15883)
This commit is contained in:
parent
9831bf4fe3
commit
002e266bae
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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",
|
||||
},
|
||||
)
|
||||
]
|
|
@ -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(',')]}
|
|
@ -116,6 +116,7 @@ INSTALLED_APPS = (
|
|||
'passerelle.apps.airquality',
|
||||
'passerelle.apps.okina',
|
||||
'passerelle.apps.cmis',
|
||||
'passerelle.apps.cityweb',
|
||||
# backoffice templates and static
|
||||
'gadjo',
|
||||
)
|
||||
|
|
1
setup.py
1
setup.py
|
@ -98,6 +98,7 @@ setup(name='passerelle',
|
|||
'pyexcel-xls >= 0.4, <0.5',
|
||||
'cmislib >= 0.5, <0.6',
|
||||
'feedparser',
|
||||
'lxml'
|
||||
],
|
||||
cmdclass={
|
||||
'build': build,
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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)
|
Loading…
Reference in New Issue