passerelle/passerelle/contrib/toulouse_maelis/models.py

1440 lines
58 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (C) 2022 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/>.
from urllib.parse import urljoin
import zeep
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.utils.timezone import now
from zeep.helpers import serialize_object
from zeep.wsse.username import UsernameToken
from passerelle.base.models import BaseResource, HTTPResource
from passerelle.utils.api import endpoint
from passerelle.utils.conversion import simplify
from passerelle.utils.jsonresponse import APIError
from . import schemas
class UpdateError(Exception):
pass
class ToulouseMaelis(BaseResource, HTTPResource):
# noqa pylint: disable=too-many-public-methods
base_wsdl_url = models.CharField(
max_length=128,
blank=False,
verbose_name='URL de base des WSDL',
default='https://demo-toulouse.sigec.fr/maelisws-toulouse/services/',
)
zeep_wsse_username = models.CharField(
max_length=64, blank=True, default='', verbose_name='Identifiant utilisateur WSSE'
)
zeep_wsse_password = models.CharField(
max_length=64, blank=True, default='', verbose_name='Mot de passe WSSE'
)
category = 'Connecteurs métiers'
_category_ordering = ['Famille', 'Activités']
class Meta:
verbose_name = 'Toulouse Maelis'
def get_client(self, wsdl_short_name):
wsse = UsernameToken(self.zeep_wsse_username, self.zeep_wsse_password)
wsdl_name = wsdl_short_name + 'Service?wsdl'
wsdl_url = urljoin(self.base_wsdl_url, wsdl_name)
settings = zeep.Settings(strict=False, xsd_ignore_sequence_order=True)
return self.soap_client(wsdl_url=wsdl_url, wsse=wsse, settings=settings)
def call(self, wsdl_short_name, service, **kwargs):
client = self.get_client(wsdl_short_name)
method = getattr(client.service, service)
try:
return method(**kwargs)
except zeep.exceptions.Fault as e:
raise APIError(e.message, err_code='%s-%s-%s' % (wsdl_short_name, service, e.code))
def check_status(self):
assert self.call('Family', 'isWSRunning')
assert self.call('Activity', 'isWSRunning')
assert self.call('Invoice', 'isWSRunning')
def update_referential(self, referential_name):
id_key, text_key = 'id', 'text'
# local referentials
if referential_name == 'Complement':
response = [
{'id': 'B', 'text': 'bis'},
{'id': 'Q', 'text': 'quater'},
{'id': 'T', 'text': 'ter'},
]
elif referential_name == 'Sex':
response = [
{'id': 'F', 'text': 'Féminin'},
{'id': 'M', 'text': 'Masculin'},
]
else:
# remote referentials
try:
response = self.call('Family', 'read' + referential_name + 'List')
response = serialize_object(response)
except Exception as e:
raise UpdateError('Service indisponible : %s' % str(e))
if referential_name == 'Organ':
id_key, text_key = 'id', 'code'
elif referential_name == 'Street':
id_key, text_key = 'idStreet', 'libelleStreet'
elif 'Indicator' in referential_name:
id_key, text_key = 'code', 'label'
else:
id_key, text_key = 'code', 'libelle'
last_update = now()
for item in response:
text = item[text_key].strip()
self.referential.update_or_create(
resource_id=self.id,
referential_name=referential_name,
item_id=item[id_key],
defaults={
'item_text': text,
'item_unaccent_text': simplify(text),
'item_data': dict({'id': item[id_key], 'text': text}, **item),
'updated': last_update,
},
)
self.referential.filter(referential_name=referential_name, updated__lt=last_update).delete()
def update_referentials(self):
for name in (
'Category',
'ChildIndicator',
'Civility',
'Complement',
'Country',
'CSP',
'DietCode',
'Organ',
'PAI',
'Quality',
'Quotient',
'RLIndicator',
'Sex',
'Situation',
'Street',
'Vaccin',
):
self.logger.info('Mise à jour du réferentiels %s' % name)
self.update_referential(name)
def daily(self):
try:
self.update_referentials()
except UpdateError as e:
self.logger.warning('Erreur sur la mise à jour: %s' % e)
else:
self.logger.info('Réferentiels mis à jour.')
def get_referential(self, referential_name, id=None, q=None, limit=None, distinct=True):
if id is not None:
queryset = self.referential.filter(referential_name=referential_name, item_id=id)
else:
queryset = self.referential.filter(referential_name=referential_name).all()
if q:
queryset = queryset.filter(item_unaccent_text__icontains=simplify(q))
if distinct:
queryset = queryset.distinct('referential_name', 'item_text')
if limit:
try:
limit = int(limit)
except ValueError:
pass
else:
queryset = queryset[:limit]
return [x.item_data for x in queryset]
def get_referential_value(self, referential_name, key):
try:
return self.referential.get(referential_name=referential_name, item_id=key).item_text
except Referential.DoesNotExist:
self.logger.warning("No '%s' key into Maelis '%s' referential", key, referential_name)
return key
def get_link(self, NameID):
try:
return self.link_set.get(name_id=NameID)
except Link.DoesNotExist:
raise APIError('User not linked to family', err_code='not-linked')
def get_family_raw(self, family_id, **kwargs):
response = self.call('Family', 'readFamily', dossierNumber=family_id, **kwargs)
data = serialize_object(response)
return data
def get_rl_raw(self, family_id, rl_id, **kwargs):
data = self.get_family_raw(family_id, **kwargs)
if data['RL1']['num'] == rl_id:
return data['RL1']
elif data['RL2'] and data['RL2']['num'] == rl_id:
return data['RL2']
raise APIError("no '%s' RL on '%s' family" % (rl_id, family_id), err_code='not-found')
def get_person_raw(self, family_id, person_id):
data = self.get_family_raw(family_id)
for person in data['emergencyPersonList']:
if str(person['numPerson']) == person_id:
return person
raise APIError(
"no '%s' emergency person on '%s' family" % (person_id, family_id), err_code='not-found'
)
def get_child_raw(self, family_id, child_id):
data = self.get_family_raw(family_id)
for child in data['childList']:
if child['num'] == child_id:
return child
raise APIError("no '%s' child on '%s' family" % (child_id, family_id), err_code='not-found')
def get_child_person_raw(self, family_id, child_id, person_id):
data = self.get_child_raw(family_id, child_id)
for person in data['authorizedPersonList']:
if str(person['personInfo']['num']) == person_id:
return person
raise APIError(
"no '%s' authorized person on '%s' child" % (person_id, child_id), err_code='not-found'
)
def add_text_value(self, referential_name, data, keys):
'''add text from referentials'''
last_key = keys.pop()
for key in keys:
if not isinstance(data, dict) or not key in data:
return
data = data[key]
if isinstance(data, dict) and last_key in data and data[last_key] is not None:
data[last_key + '_text'] = self.get_referential_value(referential_name, data[last_key])
def add_text_value_to_rl_indicator(self, data):
self.add_text_value('RLIndicator', data, ['code'])
def add_text_value_to_child_indicator(self, data):
self.add_text_value('ChildIndicator', data, ['code'])
def add_text_value_to_child_person(self, data):
self.add_text_value('Civility', data, ['personInfo', 'civility'])
self.add_text_value('Quality', data, ['personQuality', 'code'])
self.add_text_value('Sex', data, ['personInfo', 'sexe'])
return data
def add_text_value_to_child(self, data):
self.add_text_value('Sex', data, ['sexe'])
self.add_text_value('DietCode', data, ['dietcode'])
self.add_text_value('PAI', data, ['paiInfoBean', 'code'])
for person in data['authorizedPersonList']:
self.add_text_value_to_child_person(person)
for indicator in data['indicatorList']:
self.add_text_value_to_child_indicator(indicator)
# sort indicators
if data['indicatorList']:
data['indicatorList'].sort(key=lambda x: x['code'])
return data
def add_text_value_to_person(self, data):
self.add_text_value('Civility', data, ['civility'])
self.add_text_value('Quality', data, ['quality'])
self.add_text_value('Sex', data, ['sexe'])
def add_text_value_to_rl(self, data):
self.add_text_value('Civility', data, ['civility'])
self.add_text_value('Quality', data, ['quality'])
self.add_text_value('Complement', data, ['adresse', 'numComp'])
self.add_text_value('Street', data, ['adresse', 'idStreet'])
self.add_text_value('CSP', data, ['profession', 'codeCSP'])
self.add_text_value('Organ', data, ['CAFInfo', 'organ'])
for indicator in data['indicatorList']:
self.add_text_value_to_rl_indicator(indicator)
for quotient in data['quotientList']:
self.add_text_value('Quotient', quotient, ['cdquo'])
# sort indicators
if data['indicatorList']:
data['indicatorList'].sort(key=lambda x: x['code'])
def add_text_value_to_family(self, data):
self.add_text_value('Category', data, ['category'])
self.add_text_value('Situation', data, ['situation'])
for rlg in 'RL1', 'RL2':
if data.get(rlg):
self.add_text_value_to_rl(data[rlg])
for child in data['childList']:
self.add_text_value_to_child(child)
for person in data['emergencyPersonList']:
self.add_text_value_to_person(person)
return data
def get_child_person(self, family_id, child_id, person_id):
data = self.get_child_person_raw(family_id, child_id, person_id)
self.add_text_value_to_child_person(data)
return data
def get_child(self, family_id, child_id):
data = self.get_child_raw(family_id, child_id)
self.add_text_value_to_child(data)
return data
def get_person(self, family_id, person_id):
data = self.get_person_raw(family_id, person_id)
self.add_text_value_to_person(data)
return data
def get_rl(self, family_id, rl_id, **kwargs):
data = self.get_rl_raw(family_id, rl_id, **kwargs)
self.add_text_value_to_rl(data)
return data
def get_family(self, family_id, **kwargs):
data = self.get_family_raw(family_id, **kwargs)
self.add_text_value_to_family(data)
return data
def assert_key_in_referential(self, referential_name, key_value, keys_text, required=True):
if not key_value:
if required:
raise APIError("%s is required and could not be None" % keys_text, err_code='field-required')
return
try:
self.referential.get(referential_name=referential_name, item_id=key_value)
except Referential.DoesNotExist:
ref_text = "required " if required else ""
ref_text = ref_text + "referential"
raise APIError(
"%s key value '%s' do not belong to '%s' %s"
% (keys_text, key_value, referential_name, ref_text),
err_code='wrong-key',
)
def assert_post_data_in_referential(self, referential_name, data, keys, required=True):
key_value = None
for key in keys:
if not (isinstance(data, list) and isinstance(key, int)) and not (
isinstance(data, dict) and key in data
):
break
data = data[key]
else:
key_value = data
self.assert_key_in_referential(referential_name, key_value, '/'.join(str(x) for x in keys), required)
def assert_update_indicator_payload_in_referential(self, referential, post_data, parent_keys=None):
keys = parent_keys or []
data = post_data
for key in keys:
data = data[key]
for i in range(0, len(data.get('indicatorList', []))):
self.assert_post_data_in_referential(
referential, post_data, keys + ['indicatorList', i, 'code'], required=True
)
def assert_child_medical_record_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
data = post_data
for key in keys:
data = data[key]
for i in range(0, len(data.get('vaccinList', []))):
self.assert_post_data_in_referential(
'Vaccin', post_data, keys + ['vaccinList', i, 'code'], required=False
)
def assert_child_pai_payoad_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential('PAI', post_data, keys + ['code'])
def assert_child_person_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential(
'Civility', post_data, keys + ['personInfo', 'civility'], required=False
)
self.assert_post_data_in_referential('Sex', post_data, keys + ['personInfo', 'sexe'], required=False)
self.assert_post_data_in_referential('Quality', post_data, keys + ['personQuality', 'code'])
def assert_child_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential('Sex', post_data, keys + ['sexe'])
data = post_data
for key in keys:
data = data[key]
if 'dietcode' in data:
self.assert_post_data_in_referential('DietCode', post_data, keys + ['dietcode'], required=False)
if 'paiInfoBean' in data:
self.assert_child_pai_payoad_in_referential(post_data, keys + ['paiInfoBean'])
if 'medicalRecord' in data:
# dead code as updateFamily seems not to modify medicalRecord
self.assert_child_medical_record_payload_in_referential(post_data, keys + ['medicalRecord'])
self.assert_update_indicator_payload_in_referential('ChildIndicator', post_data, keys)
def assert_person_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential('Civility', post_data, keys + ['civility'], required=False)
self.assert_post_data_in_referential('Sex', post_data, keys + ['sexe'], required=False)
self.assert_post_data_in_referential('Quality', post_data, keys + ['quality'])
def assert_update_coordinate_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential(
'Street', post_data, keys + ['adresse', 'idStreet'], required=False
)
self.assert_post_data_in_referential(
'Complement', post_data, keys + ['adresse', 'numComp'], required=False
)
self.assert_post_data_in_referential(
'CSP', post_data, keys + ['profession', 'codeCSP'], required=False
)
self.assert_post_data_in_referential('Organ', post_data, keys + ['CAFInfo', 'organ'], required=False)
def assert_rl_payload_in_referential(self, post_data, parent_keys=None):
keys = parent_keys or []
self.assert_post_data_in_referential('Civility', post_data, keys + ['civility'])
self.assert_post_data_in_referential('Quality', post_data, keys + ['quality'])
self.assert_update_coordinate_payload_in_referential(post_data, keys)
self.assert_update_indicator_payload_in_referential('RLIndicator', post_data, keys)
def assert_create_rl1_payload_in_referential(self, post_data):
self.assert_post_data_in_referential('Category', post_data, ['category'])
self.assert_post_data_in_referential('Situation', post_data, ['situation'])
self.assert_rl_payload_in_referential(post_data, ['rl1'])
def assert_family_payload_in_referential(self, post_data):
self.assert_post_data_in_referential('Category', post_data, ['category'])
self.assert_post_data_in_referential('Situation', post_data, ['situation'])
for rlg in 'rl1', 'rl2':
if rlg in post_data:
self.assert_rl_payload_in_referential(post_data, [rlg])
for i, person in enumerate(post_data.get('emergencyPersonList') or []):
for j in range(0, len(person.get('personList') or [])):
self.assert_person_payload_in_referential(
post_data, ['emergencyPersonList', i, 'personList', j]
)
for i in range(0, len(post_data.get('childList') or [])):
self.assert_child_payload_in_referential(post_data, ['childList', i])
def replace_null_values(self, dico):
'''send null fields as empty SOAP tag to tell maelis to empty the value'''
for key, value in dico.items():
if isinstance(value, dict):
self.replace_null_values(value)
if value is None:
dico[key] = ''
@endpoint(
display_category='Famille',
description='Liste des catégories',
name='read-category-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_category_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Category', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des indicateurs sur le enfants',
name='read-child-indicator-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_child_indicator_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('ChildIndicator', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des civilités',
name='read-civility-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_civility_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Civility', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des compléments du numéro de voie',
name='read-complement-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_complement_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Complement', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des pays',
name='read-country-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_country_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Country', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='liste des catégories socio-professionnelles',
name='read-csp-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_csp_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('CSP', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des régimes alimentaires',
name='read-dietcode-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_dietcode_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('DietCode', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des organismes (CAF)',
name='read-organ-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_organ_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Organ')}
@endpoint(
display_category='Famille',
description="Liste des projet d'accueil individualisés",
name='read-pai-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_pai_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('PAI', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='liste des qualités du référenciel',
name='read-quality-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_quality_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Quality', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des quotients',
name='read-quotient-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_quotient_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Quotient', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des indicateurs sur les responsables légaux',
name='read-rl-indicator-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_rl_indicator_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('RLIndicator')}
@endpoint(
display_category='Famille',
description='Liste des sexes',
name='read-sex-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_sex_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Sex', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='liste des situations',
name='read-situation-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_situation_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Situation', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='liste des voies',
name='read-street-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_street_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Street', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Liste des vaccins',
name='read-vaccin-list',
perm='can_access',
parameters={
'id': {'description': 'Identifiant de lenregistrement'},
'q': {'description': 'Recherche en texte intégral'},
'limit': {'description': 'Nombre maximal de résultats; doit être inférieur à 20.'},
'distinct': {'description': 'Supression des doublons'},
},
)
def read_vaccin_list(self, request, id=None, q=None, limit=None, distinct=True):
return {'data': self.get_referential('Vaccin', id, q, limit, distinct)}
@endpoint(
display_category='Famille',
description='Lier un compte usager à une famille',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.LINK_SCHEMA}}},
)
def link(self, request, NameID, post_data):
family_id = post_data['family_id']
response = self.call('Family', 'readFamily', dossierNumber=family_id)
if not (
response['RL1']['firstname'] == post_data['firstname'].upper()
and response['RL1']['lastname'] == post_data['lastname'].upper()
and response['RL1']['birth']['dateBirth'].strftime('%Y-%m-%d') == post_data['dateBirth']
):
raise APIError("RL1 does not match '%s' family" % family_id, err_code='not-found')
Link.objects.update_or_create(resource=self, name_id=NameID, defaults={'family_id': family_id})
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description='Supprimer une liaison entre un compte usager et une famille',
methods=['post'],
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
},
)
def unlink(self, request, NameID):
link = self.get_link(NameID)
link.delete()
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description='Informations sur la famille',
perm='can_access',
name='read-family',
parameters={
'NameID': {'description': 'Publik NameID'},
'income_year': {'description': 'Année de revenu pour filtrer les quotients'},
},
)
def read_family(self, request, NameID, income_year=None):
family_id = self.get_link(NameID).family_id
data = self.get_family(family_id, incomeYear=income_year)
return {'data': data}
@endpoint(
display_category='Famille',
description="Informations sur un responsable légal",
perm='can_access',
name='read-rl',
parameters={
'NameID': {'description': 'Publik NameID'},
'rl_id': {'description': 'Numéro du représentant légal'},
'income_year': {'description': 'Année de revenu pour filtrer les quotients'},
},
)
def read_rl(self, request, NameID, rl_id, income_year=None):
family_id = self.get_link(NameID).family_id
data = self.get_rl(family_id, rl_id, incomeYear=income_year)
return {'data': data}
@endpoint(
display_category='Famille',
description="Informations sur une personne autorisée à récupérer les enfants ou à prévenir en cas d'urgence",
perm='can_access',
name='read-person',
parameters={
'NameID': {'description': 'Publik NameID'},
'person_id': {'description': 'Numéro de la personne'},
},
)
def read_person(self, request, NameID, person_id):
family_id = self.get_link(NameID).family_id
data = self.get_person(family_id, person_id)
return {'data': data}
@endpoint(
display_category='Famille',
description="Informations sur un enfant",
perm='can_access',
name='read-child',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
)
def read_child(self, request, NameID, child_id):
family_id = self.get_link(NameID).family_id
data = self.get_child(family_id, child_id)
return {'data': data}
@endpoint(
display_category='Famille',
description="Informations sur une personne autorisée à récupérer l'enfant",
perm='can_access',
name='read-child-person',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
'person_id': {'description': 'Numéro de la personne'},
},
)
def read_child_person(self, request, NameID, child_id, person_id):
family_id = self.get_link(NameID).family_id
data = self.get_child_person(family_id, child_id, person_id)
return {'data': data}
@endpoint(
display_category='Famille',
description="Vérifier qu'un responsable légal existe en base",
perm='can_access',
name='is-rl-exists',
post={'request_body': {'schema': {'application/json': schemas.ISEXISTS_SCHEMA}}},
)
def is_rl_exists(self, request, post_data):
response = self.call('Family', 'isRLExists', **post_data)
return {'data': response}
@endpoint(
display_category='Famille',
description="Vérifier qu'un responsable légal existe en base",
perm='can_access',
name='is-child-exists',
post={'request_body': {'schema': {'application/json': schemas.ISEXISTS_SCHEMA}}},
)
def is_child_exists(self, request, post_data):
response = self.call('Family', 'isChildExists', **post_data)
return {'data': response}
@endpoint(
display_category='Famille',
description='Création de la famille',
name='create-family',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.CREATE_FAMILY_SCHEMA}}},
)
def create_family(self, request, NameID, post_data):
if self.link_set.filter(name_id=NameID).exists():
raise APIError('User already linked to family', err_code='already-linked')
self.assert_family_payload_in_referential(post_data)
response = self.call('Family', 'createFamily', **post_data)
data = serialize_object(response)
family_id = data.get('number')
if not family_id:
errors = data.get('rl1ErrorList') + data.get('childErrorList')
err_codes = [x.split(':')[0][:4] for x in errors]
raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
Link.objects.create(resource=self, name_id=NameID, family_id=family_id)
return {'data': data}
@endpoint(
display_category='Famille',
description='Modification de la famille',
name='update-family',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_FAMILY_SCHEMA}}},
)
def update_family(self, request, NameID, post_data):
family_id = self.get_link(NameID).family_id
self.assert_family_payload_in_referential(post_data)
self.replace_null_values(post_data)
# adapt payload to use same input as create_family
if len(post_data.get('emergencyPersonList', [])):
persons = post_data.pop('emergencyPersonList')
post_data['emergencyPersonList'] = [{'personList': persons}]
response = self.call('Family', 'updateFamily', dossierNumber=family_id, **post_data)
data = serialize_object(response)
family_id = data.get('number')
errors = data.get('childErrorList')
if errors:
err_codes = [x.split(':')[0][:4] for x in errors]
raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
return {'data': data}
@endpoint(
display_category='Famille',
description='Création du RL1',
name='create-rl1',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.CREATE_RL1_SCHEMA}}},
)
def create_rl1(self, request, NameID, post_data):
if self.link_set.filter(name_id=NameID).exists():
raise APIError('User already linked to family', err_code='already-linked')
self.assert_create_rl1_payload_in_referential(post_data)
response = self.call('Family', 'createFamily', **post_data)
data = serialize_object(response)
family_id = data.get('number')
if not family_id:
errors = data.get('rl1ErrorList') or []
raise APIError(' ; '.join(errors), err_code='already-rl1')
Link.objects.create(resource=self, name_id=NameID, family_id=family_id)
return {'data': {'family_id': family_id}}
@endpoint(
display_category='Famille',
description='Modification du RL1',
name='update-rl1',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_RL1_SCHEMA}}},
)
def update_rl1(self, request, NameID, post_data):
family_id = self.get_link(NameID).family_id
self.assert_rl_payload_in_referential(post_data)
self.replace_null_values(post_data)
family = self.get_family_raw(family_id)
rl1 = post_data
rl1['adresse'] = family['RL1']['adresse']
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'rl1': rl1,
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description='Création du RL2',
name='create-rl2',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.CREATE_RL2_SCHEMA}}},
)
def create_rl2(self, request, NameID, post_data):
family_id = self.get_link(NameID).family_id
self.assert_rl_payload_in_referential(post_data)
family = self.get_family_raw(family_id)
if family['RL2']:
raise APIError('RL2 already defined on family', err_code='already-rl2')
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'rl2': post_data,
}
response = self.call('Family', 'updateFamily', **payload)
return {'data': {'id': response['RL2']['num']}}
@endpoint(
display_category='Famille',
description='Modification du RL2',
name='update-rl2',
perm='can_access',
parameters={'NameID': {'description': 'Publik NameID'}},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_RL2_SCHEMA}}},
)
def update_rl2(self, request, NameID, post_data):
family_id = self.get_link(NameID).family_id
self.assert_rl_payload_in_referential(post_data)
self.replace_null_values(post_data)
family = self.get_family_raw(family_id)
if not family['RL2']:
raise APIError('No RL2 to update on family', err_code='no-rl2')
rl2 = post_data
rl2['adresse'] = family['RL2']['adresse']
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'rl2': rl2,
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Ajout d'un enfant",
name='create-child',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'force': {
'description': 'boolean to bypass doublon error',
'type': 'bool',
'example_value': 'false',
},
},
post={'request_body': {'schema': {'application/json': schemas.CREATE_CHILD_SCHEMA}}},
)
def create_child(self, request, NameID, post_data, force=False):
family_id = self.get_link(NameID).family_id
self.assert_child_payload_in_referential(post_data)
payload = {
'numDossier': family_id,
'isForceCreateChild': force,
'child': post_data,
}
response = self.call('Family', 'createChild', **payload)
data = serialize_object(response)
child_id = data.get('number')
if not child_id:
errors = data.get('childErrorList') or []
raise APIError(' ; '.join(errors), err_code='already-child')
return {'data': {'child_id': child_id}}
@endpoint(
display_category='Famille',
description="Modification d'un enfant",
name='update-child',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_CHILD_SCHEMA}}},
)
def update_child(self, request, NameID, child_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_child_payload_in_referential(post_data)
self.replace_null_values(post_data)
family = self.get_family_raw(family_id)
child = post_data
child['num'] = child_id
for known_child in family['childList']:
if str(known_child['num']) == child_id:
child['authorizedPersonList'] = known_child['authorizedPersonList']
break
else:
raise APIError('No child %s to update on family' % child_id, err_code='no-child')
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'childList': [child],
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Mise à jour des coordonnées d'un responsable légal",
name='update-coordinate',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'rl_id': {'description': 'Numéro du représentant légal'},
},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_COORDINATE_SCHEMA}}},
)
def update_coordinate(self, request, NameID, rl_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_update_coordinate_payload_in_referential(post_data)
self.replace_null_values(post_data)
self.call('Family', 'updateCoordinate', numDossier=family_id, numPerson=rl_id, **post_data)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Mise à jour des indicateurs d'un responsable légal",
name='update-rl-indicator',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'rl_id': {'description': 'Numéro du représentant légal'},
},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_INDICATOR_SCHEMA}}},
)
def update_rl_indicator(self, request, NameID, rl_id, post_data):
self.get_link(NameID)
self.assert_update_indicator_payload_in_referential('RLIndicator', post_data)
self.call('Family', 'updatePersonIndicatorList', numPerson=rl_id, **post_data)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Créer ou mettre à jour un quotient d'un responsable légal",
name='update-quotient',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'rl_id': {'description': "Numéro du responsable légal"},
},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_QUOTIENT_SCHEMA}}},
)
def update_quotient(self, request, NameID, rl_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_post_data_in_referential('Quotient', post_data, ['cdquo'])
payload = {
'dossierNumber': family_id,
'personNumber': rl_id,
'quotient': post_data,
}
self.call('Family', 'createUpdateQuotient', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Création d'une personne à prévenir en cas d'urgence",
name='create-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
},
post={'request_body': {'schema': {'application/json': schemas.EMERGENCY_PERSON_SCHEMA}}},
)
def create_person(self, request, NameID, post_data):
family_id = self.get_link(NameID).family_id
self.assert_person_payload_in_referential(post_data)
family = self.get_family_raw(family_id)
personList = family['emergencyPersonList']
personList.append(post_data)
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'emergencyPersonList': [{'personList': personList}],
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Mise à jour d'une personne à prévenir en cas d'urgence",
name='update-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'person_id': {'description': 'Numéro de la personne'},
},
post={'request_body': {'schema': {'application/json': schemas.EMERGENCY_PERSON_SCHEMA}}},
)
def update_person(self, request, NameID, person_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_person_payload_in_referential(post_data)
family = self.get_family_raw(family_id)
personList = family['emergencyPersonList']
for i, person in enumerate(personList):
if str(person['numPerson']) == person_id:
personList[i] = post_data
personList[i]['numPerson'] = person_id
break
else:
raise APIError(
"no '%s' authorized person on '%s' family" % (person_id, family_id), err_code='not-found'
)
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'emergencyPersonList': [{'personList': personList}],
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Suppression d'une personne à prévenir en cas d'urgence",
name='delete-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'person_id': {'description': 'Numéro de la personne'},
},
methods=['post'],
)
def delete_person(self, request, NameID, person_id):
family_id = self.get_link(NameID).family_id
family = self.get_family_raw(family_id)
personList = family['emergencyPersonList']
for i, person in enumerate(personList):
if str(person['numPerson']) == person_id:
del personList[i]
break
else:
raise APIError(
"no '%s' authorized person on '%s' family" % (person_id, family_id), err_code='not-found'
)
payload = {
'dossierNumber': family_id,
'category': family['category'],
'situation': family['situation'],
'flagCom': family['flagCom'],
'nbChild': family['nbChild'],
'nbTotalChild': family['nbTotalChild'],
'nbAES': family['nbAES'],
'emergencyPersonList': [{'personList': personList}],
}
self.call('Family', 'updateFamily', **payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Création d'une personne autorisée à récupérer l'enfant",
name='create-child-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
post={'request_body': {'schema': {'application/json': schemas.AUTHORIZED_PERSON_SCHEMA}}},
)
def create_child_person(self, request, NameID, child_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_child_person_payload_in_referential(post_data)
child = self.get_child_raw(family_id, child_id)
personList = child['authorizedPersonList']
personList.append(post_data)
req = {
'numFamily': family_id,
'numPerson': child_id,
'bLeaveAlone': child['bLeaveAlone'],
'bPhoto': child['bPhoto'],
'personList': personList,
}
self.call('Family', 'updateChildAutorization', updateChildAutorizationRequest=req)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Mise à jour d'une personne autorisée à récupérer l'enfant",
name='update-child-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
'person_id': {'description': 'Numéro de la personne'},
},
post={'request_body': {'schema': {'application/json': schemas.AUTHORIZED_PERSON_SCHEMA}}},
)
def update_child_person(self, request, NameID, child_id, person_id, post_data):
family_id = self.get_link(NameID).family_id
self.assert_child_person_payload_in_referential(post_data)
child = self.get_child_raw(family_id, child_id)
personList = child['authorizedPersonList']
for i, person in enumerate(personList):
if str(person['personInfo']['num']) == person_id:
personList[i] = post_data
personList[i]['personInfo']['num'] = person_id
break
else:
raise APIError(
"No '%s' authorized person on '%s' child" % (person_id, child_id), err_code='not-found'
)
req = {
'numFamily': family_id,
'numPerson': child_id,
'bLeaveAlone': child['bLeaveAlone'],
'bPhoto': child['bPhoto'],
'personList': personList,
}
self.call('Family', 'updateChildAutorization', updateChildAutorizationRequest=req)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Suppression d'une personne autorisée à récupérer l'enfant",
name='delete-child-person',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
'person_id': {'description': 'Numéro de la personne'},
},
methods=['post'],
)
def delete_child_person(self, request, NameID, child_id, person_id):
family_id = self.get_link(NameID).family_id
child = self.get_child_raw(family_id, child_id)
personList = child['authorizedPersonList']
for i, person in enumerate(personList):
if str(person['personInfo']['num']) == person_id:
del personList[i]
break
else:
raise APIError(
"No '%s' authorized person on '%s' child" % (person_id, child_id), err_code='not-found'
)
req = {
'numFamily': family_id,
'numPerson': child_id,
'bLeaveAlone': child['bLeaveAlone'],
'bPhoto': child['bPhoto'],
'personList': personList,
}
self.call('Family', 'updateChildAutorization', updateChildAutorizationRequest=req)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Créer ou mettre à jour le régime alimentaire d'un enfant",
name='update-child-dietcode',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
'dietcode': {'description': 'code du régime alimentaire'},
},
methods=['post'],
)
def update_child_dietcode(self, request, NameID, child_id, dietcode):
self.get_link(NameID)
self.assert_key_in_referential('DietCode', dietcode, 'dietcode parameter', required=False)
self.call('Family', 'createOrUpdateChildDiet', personNumber=child_id, code=dietcode)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Créer ou mettre à jour les informations relatives au PAI d'un enfant",
name='update-child-pai',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
post={'request_body': {'schema': {'application/json': schemas.PAIINFO_SCHEMA}}},
)
def update_child_pai(self, request, NameID, child_id, post_data):
self.get_link(NameID)
self.assert_child_pai_payoad_in_referential(post_data)
# use None to empty date passed as an empty string by date filter
for key in 'dateDeb', 'dateFin':
if post_data[key] == '':
post_data[key] = None
self.call('Family', 'updateChildPAI', personNumber=child_id, **post_data)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Créer ou mettre à jour les données médicales d'un enfant",
name='update-child-medical-record',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
post={'request_body': {'schema': {'application/json': schemas.MEDICALRECORD_SCHEMA}}},
)
def update_child_medical_record(self, request, NameID, child_id, post_data):
self.get_link(NameID)
self.assert_child_medical_record_payload_in_referential(post_data)
self.replace_null_values(post_data)
payload = {
'numPerson': child_id,
'medicalRecord': post_data,
}
self.call('Family', 'updateChildMedicalRecord', updateChildMedicalRecordRequest=payload)
return {'data': 'ok'}
@endpoint(
display_category='Famille',
description="Mise à jour des indicateurs d'un enfant",
name='update-child-indicator',
perm='can_access',
parameters={
'NameID': {'description': 'Publik NameID'},
'child_id': {'description': "Numéro de l'enfant"},
},
post={'request_body': {'schema': {'application/json': schemas.UPDATE_INDICATOR_SCHEMA}}},
)
def update_child_indicator(self, request, NameID, child_id, post_data):
self.get_link(NameID)
self.assert_update_indicator_payload_in_referential('ChildIndicator', post_data)
self.call('Family', 'updatePersonIndicatorList', numPerson=child_id, **post_data)
return {'data': 'ok'}
class Link(models.Model):
resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE)
name_id = models.CharField(blank=False, max_length=256)
family_id = models.CharField(blank=False, max_length=128)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('resource', 'name_id')
class Referential(models.Model):
resource = models.ForeignKey(
verbose_name='Resource',
to=ToulouseMaelis,
on_delete=models.CASCADE,
related_name='referential',
)
referential_name = models.CharField('Name', max_length=64)
item_id = models.CharField('Key', max_length=64)
item_text = models.CharField('Text', max_length=128)
item_unaccent_text = models.CharField('Text', max_length=64, null=True)
item_data = JSONField('Data')
created = models.DateTimeField('Created', auto_now_add=True)
updated = models.DateTimeField('Updated', auto_now=True)
def __repr__(self):
return '<Referential "%s/%s">' % (self.referential_name, self.item_id)
class Meta:
ordering = ('referential_name', 'item_text', 'item_id')
unique_together = [['referential_name', 'item_id']]