passerelle/passerelle/contrib/toulouse_axel/utils.py

233 lines
6.7 KiB
Python

# -*- coding: utf-8 -*-
# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2020 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 __future__ import unicode_literals
from collections import OrderedDict
import datetime
import unicodedata
import xml.etree.ElementTree as ET
import pytz
from django.utils.six import string_types
from passerelle.utils.conversion import normalize
boolean_type = {
'oneOf': [
{'type': 'boolean'},
{
'type': 'string',
'pattern': '[Oo][Uu][Ii]|[Nn][Oo][Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0',
}
]
}
date_type = {
'type': 'string',
'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}',
}
datetime_type = {
'type': 'string',
'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}',
}
json_date_format = '%Y-%m-%d'
json_datetime_format = '%Y-%m-%dT%H:%M:%S'
xml_date_format = '%d/%m/%Y'
xml_datetime_format = '%d/%m/%Y %H:%M:%S'
situation_familiale_mapping = OrderedDict([
('C', 'Célibataire'),
('D', 'Divorcé (e)'),
('M', 'Marié (e)'),
('S', 'Séparé (e)'),
('V', 'Veuf (ve)'),
('VM', 'Vie Maritale'),
('P', 'Pacs'),
])
csp_mapping = OrderedDict([
('OUV', 'Ouvriers'),
('EMP', 'Employés'),
('ETU', 'Etudiants'),
('RET', 'Retraités'),
('STA', 'Personnes en stage'),
('AGEX', 'Agriculteurs exploitants'),
('ARCO', 'Artisans commercants chefs entreprise'),
('CADP', 'Cadres professions intellectuelles supérieures'),
('PRIN', 'Professions intermediaires'),
('SACT', 'Autres personnes sans activité professionnelle'),
('REC', 'Recherche emploi'),
])
lien_parente_mapping = OrderedDict([
('GRP1', 'Grands-parents paternels'),
('GRP2', 'Grands-parents maternels'),
('VOI', 'Voisin'),
('FRE', "Frère de l'enfant"),
('SOE', "Soeur de l'enfant"),
('AMI', 'Ami (e)'),
('FAMI', 'Membre de la famille'),
('BABY', 'Baby sitter'),
])
type_regime_mapping = OrderedDict([
('GENE', 'Régime général'),
('ZAU', 'Autre'),
('MSA', 'MSA'),
])
regime_mapping = OrderedDict([
('AV', 'Menu avec viande'),
('SV', 'Menu sans viande'),
])
def get_label(mapping, code):
return mapping.get(code, code)
def indent(tree, space=" ", level=0):
# backport from Lib/xml/etree/ElementTree.py python 3.9
if isinstance(tree, ET.ElementTree):
tree = tree.getroot()
if level < 0:
raise ValueError("Initial indentation level must be >= 0, got {level}".format(level))
if not len(tree):
return
# Reduce the memory consumption by reusing indentation strings.
indentations = ["\n" + level * space]
def _indent_children(elem, level):
# Start a new indentation level for the first child.
child_level = level + 1
try:
child_indentation = indentations[child_level]
except IndexError:
child_indentation = indentations[level] + space
indentations.append(child_indentation)
if not elem.text or not elem.text.strip():
elem.text = child_indentation
for child in elem:
if len(child):
_indent_children(child, child_level)
if not child.tail or not child.tail.strip():
child.tail = child_indentation
# Dedent after the last child by overwriting the previous indentation.
if not child.tail.strip():
child.tail = indentations[level]
_indent_children(tree, 0)
def encode_bool(obj):
if obj is True or str(obj).lower() in ['true', 'oui', '1']:
return 'OUI'
if obj is False or str(obj).lower() in ['false', 'non', '0']:
return 'NON'
return obj
def parse_datetime(value):
try:
dt = datetime.datetime.strptime(value, json_datetime_format)
except ValueError:
return None
return pytz.utc.localize(dt)
def encode_datetime(dt):
return dt.astimezone(pytz.timezone('Europe/Paris')).strftime(xml_datetime_format)
def upperize(data):
if isinstance(data, dict):
for key, val in data.items():
data[key] = upperize(val)
if isinstance(data, list):
for i, val in enumerate(data):
data[i] = upperize(val)
if isinstance(data, string_types):
data = normalize(data).upper()
return data
def normalize_invoice(invoice, dui, historical=False, vendor_base=None):
vendor = vendor_base or {}
vendor.update(invoice)
invoice_id = '%s-%s' % (dui, invoice['IDFACTURE'])
if historical:
invoice_id = 'historical-%s' % invoice_id
data = {
'id': invoice_id,
'display_id': str(invoice['IDFACTURE']),
'label': invoice['LIBELLE'],
'paid': False,
'vendor': {'toulouse-axel': vendor},
}
if historical:
data.update({
'amount': 0,
'total_amount': invoice['MONTANT'],
'created': invoice['EMISSION'],
'pay_limit_date': '',
'online_payment': False,
'has_pdf': invoice['IPDF'] == 'O',
})
else:
data.update({
'amount': invoice['RESTEAPAYER'],
'total_amount': invoice['MONTANTTOTAL'],
'amount_paid': max(0, invoice['MONTANTTOTAL'] - invoice['RESTEAPAYER']) or '',
'created': invoice['DATEEMISSION'],
'pay_limit_date': invoice['DATEECHEANCE'],
'online_payment': bool(invoice['RESTEAPAYER'] > 0),
'has_pdf': invoice['EXISTEPDF'] == '1',
})
return data
def get_reference_year_from_date(booking_date):
if booking_date.month <= 7:
# between january and july, reference year is the year just before
return booking_date.year - 1
return booking_date.year
def get_week_dates_from_date(booking_date):
day = booking_date.weekday()
# return monday and friday of the week
return (
booking_date - datetime.timedelta(days=day),
booking_date + datetime.timedelta(days=4 - day)
)
def get_booking(value):
# 0: no registration
# 1: registration
# 2: not applicable (example: GARDERIE is applicable only on wednesday)
if value == '2':
return None
return value == '1'