Projet

Général

Profil

0002-toulouse_axel-move-some-functions-in-utils-39126.patch

Lauréline Guérin, 27 janvier 2020 11:22

Télécharger (14,6 ko)

Voir les différences:

Subject: [PATCH 2/3] toulouse_axel: move some functions in utils (#39126)

 passerelle/contrib/toulouse_axel/models.py | 134 +++------------------
 passerelle/contrib/toulouse_axel/utils.py  | 124 +++++++++++++++++++
 2 files changed, 139 insertions(+), 119 deletions(-)
 create mode 100644 passerelle/contrib/toulouse_axel/utils.py
passerelle/contrib/toulouse_axel/models.py
1 1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2019  Entr'ouvert
2
# Copyright (C) 2020  Entr'ouvert
3 3
#
4 4
# This program is free software: you can redistribute it and/or modify it
5 5
# under the terms of the GNU Affero General Public License as published
......
36 36
from passerelle.utils.api import endpoint
37 37
from passerelle.utils.jsonresponse import APIError
38 38
from passerelle.utils.xml import JSONSchemaFromXMLSchema
39
from . import utils
39 40

  
40 41
logger = logging.getLogger('passerelle.contrib.toulouse_axel')
41 42

  
42 43
BASE_XSD_PATH = os.path.join(os.path.dirname(__file__), 'xsd')
43 44

  
44 45

  
45
boolean_type = {
46
    'oneOf': [
47
        {'type': 'boolean'},
48
        {
49
            'type': 'string',
50
            'pattern': '[Oo][Uu][Ii]|[Nn][Oo][Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0',
51
        }
52
    ]
53
}
54
datetime_type = {
55
    'type': 'string',
56
    'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}',
57
}
58
json_date_format = '%Y-%m-%d'
59
json_datetime_format = '%Y-%m-%dT%H:%M:%S'
60
xml_date_format = '%d/%m/%Y'
61
xml_datetime_format = '%d/%m/%Y %H:%M:%S'
62

  
63

  
64 46
PAYMENT_SCHEMA = {
65 47
    'type': 'object',
66 48
    'properties': {
67
        'transaction_date': copy.deepcopy(datetime_type),
49
        'transaction_date': copy.deepcopy(utils.datetime_type),
68 50
        'transaction_id': {
69 51
            'type': 'string',
70 52
        }
......
73 55
}
74 56

  
75 57

  
76
def indent(tree, space="  ", level=0):
77
    # backport from Lib/xml/etree/ElementTree.py python 3.9
78
    if isinstance(tree, ET.ElementTree):
79
        tree = tree.getroot()
80
    if level < 0:
81
        raise ValueError("Initial indentation level must be >= 0, got {level}".format(level))
82
    if not len(tree):
83
        return
84

  
85
    # Reduce the memory consumption by reusing indentation strings.
86
    indentations = ["\n" + level * space]
87

  
88
    def _indent_children(elem, level):
89
        # Start a new indentation level for the first child.
90
        child_level = level + 1
91
        try:
92
            child_indentation = indentations[child_level]
93
        except IndexError:
94
            child_indentation = indentations[level] + space
95
            indentations.append(child_indentation)
96

  
97
        if not elem.text or not elem.text.strip():
98
            elem.text = child_indentation
99

  
100
        for child in elem:
101
            if len(child):
102
                _indent_children(child, child_level)
103
            if not child.tail or not child.tail.strip():
104
                child.tail = child_indentation
105

  
106
        # Dedent after the last child by overwriting the previous indentation.
107
        if not child.tail.strip():
108
            child.tail = indentations[level]
109

  
110
    _indent_children(tree, 0)
111

  
112

  
113
def encode_bool(obj):
114
    if obj is True or str(obj).lower() in ['true', 'oui', '1']:
115
        return 'OUI'
116
    if obj is False or str(obj).lower() in ['false', 'non', '0']:
117
        return 'NON'
118
    return obj
119

  
120

  
121
def encode_datetime(obj):
122
    try:
123
        return datetime.datetime.strptime(obj, json_datetime_format).strftime(xml_datetime_format)
124
    except ValueError:
125
        return obj
126

  
127

  
128 58
class AxelSchema(JSONSchemaFromXMLSchema):
129 59
    type_map = {
130 60
        '{urn:AllAxelTypes}DATEREQUIREDType': 'date',
......
142 72

  
143 73
    def encode_date(self, obj):
144 74
        try:
145
            return datetime.datetime.strptime(obj, json_date_format).strftime(xml_date_format)
75
            return datetime.datetime.strptime(obj, utils.json_date_format).strftime(utils.xml_date_format)
146 76
        except ValueError:
147 77
            return obj
148 78

  
......
152 82
        return self.encode_date(obj)
153 83

  
154 84
    def decode_date(self, data):
155
        value = datetime.datetime.strptime(data.text, xml_date_format).strftime(json_date_format)
85
        value = datetime.datetime.strptime(data.text, utils.xml_date_format).strftime(utils.json_date_format)
156 86
        return xmlschema.ElementData(tag=data.tag, text=value, content=data.content, attributes=data.attributes)
157 87

  
158 88
    def decode_date_optional(self, data):
......
162 92

  
163 93
    @classmethod
164 94
    def schema_bool(cls):
165
        return copy.deepcopy(boolean_type)
95
        return copy.deepcopy(utils.boolean_type)
166 96

  
167 97
    def encode_bool(self, obj):
168
        return encode_bool(obj)
98
        return utils.encode_bool(obj)
169 99

  
170 100
    def decode_bool(self, data):
171 101
        value = False
......
236 166
                serialized_request = self.request_converter.encode(request_data)
237 167
            except xmlschema.XMLSchemaValidationError as e:
238 168
                raise AxelError('invalid request %s' % str(e))
239
            indent(serialized_request)
169
            utils.indent(serialized_request)
240 170
            serialized_request = ET.tostring(serialized_request)
241 171
            try:
242 172
                self.request_converter.xml_schema.validate(serialized_request)
......
251 181
            '')  # FIXME: What is the user parameter for ?
252 182

  
253 183
        xml_result = ET.fromstring(result.encode('utf-8'))
254
        indent(xml_result)
184
        utils.indent(xml_result)
255 185
        pretty_result = ET.tostring(xml_result)
256 186
        if xml_result.find('RESULTAT/STATUS').text != 'OK':
257 187
            msg = xml_result.find('RESULTAT/COMMENTAIRES').text
......
335 265
        form_maj_famille_dui.request_schema['properties']['PORTAIL']['properties']['DUI'])
336 266

  
337 267
    for flag in sorted(UPDATE_FAMILY_FLAGS.keys()):
338
        flag_type = copy.deepcopy(boolean_type)
268
        flag_type = copy.deepcopy(utils.boolean_type)
339 269
        if flag not in UPDATE_FAMILY_REQUIRED_FLAGS:
340 270
            flag_type['oneOf'].append({'type': 'null'})
341 271
            flag_type['oneOf'].append({'type': 'string', 'enum': ['']})
......
384 314
        'required': ['ASTHME', 'MEDICAMENTEUSES', 'ALIMENTAIRES', 'AUTRES'],
385 315
    }
386 316
    for key in ['ASTHME', 'MEDICAMENTEUSES', 'ALIMENTAIRES']:
387
        sanitaire_properties['ALLERGIE']['properties'][key] = copy.deepcopy(boolean_type)
317
        sanitaire_properties['ALLERGIE']['properties'][key] = copy.deepcopy(utils.boolean_type)
388 318
    sanitaire_properties['ALLERGIE']['properties']['AUTRES'] = {
389 319
        'oneOf': [
390 320
            {'type': 'null'},
......
576 506
        flags = sorted(self.UPDATE_FAMILY_FLAGS.keys())
577 507
        for flag in flags:
578 508
            flag_value = post_data.get(flag)
579
            flag_value = encode_bool(flag_value)
509
            flag_value = utils.encode_bool(flag_value)
580 510

  
581 511
            # no update for the related block
582 512
            if flag_value == 'OUI':
......
754 684
            }
755 685
        }
756 686

  
757
    def normalize_invoice(self, invoice, dui, historical=False, vendor_base=None):
758
        vendor = vendor_base or {}
759
        vendor.update(invoice)
760
        invoice_id = '%s-%s' % (dui, invoice['IDFACTURE'])
761
        if historical:
762
            invoice_id = 'historical-%s' % invoice_id
763
        data = {
764
            'id': invoice_id,
765
            'display_id': str(invoice['IDFACTURE']),
766
            'label': invoice['LIBELLE'],
767
            'paid': False,
768
            'vendor': {'toulouse-axel': vendor},
769
        }
770
        if historical:
771
            data.update({
772
                'amount': 0,
773
                'total_amount': invoice['MONTANT'],
774
                'created': invoice['EMISSION'],
775
                'pay_limit_date': '',
776
                'online_payment': False,
777
                'has_pdf': invoice['IPDF'] == '1',
778
            })
779
        else:
780
            data.update({
781
                'amount': invoice['RESTEAPAYER'],
782
                'total_amount': invoice['MONTANTTOTAL'],
783
                'created': invoice['DATEEMISSION'],
784
                'pay_limit_date': invoice['DATEECHEANCE'],
785
                'has_pdf': invoice['EXISTEPDF'] == '1',
786
            })
787
            pay_limit_date = datetime.datetime.strptime(invoice['DATEECHEANCE'], '%Y-%m-%d').date()
788
            data['online_payment'] = data['amount'] > 0 and pay_limit_date >= datetime.date.today()
789
        return data
790

  
791 687
    def get_invoices(self, regie_id, dui=None, name_id=None):
792 688
        assert name_id or dui
793 689
        if name_id:
......
807 703
        for facture in data.get('FACTURES', []):
808 704
            if facture['IDREGIE'] != regie_id:
809 705
                continue
810
            result.append(self.normalize_invoice(facture, dui))
706
            result.append(utils.normalize_invoice(facture, dui))
811 707
        return result
812 708

  
813 709
    def get_historical_invoices(self, name_id):
......
828 724
        for direction in data.get('DIRECTION', []):
829 725
            for facture in direction.get('FACTURE', []):
830 726
                result.append(
831
                    self.normalize_invoice(
727
                    utils.normalize_invoice(
832 728
                        facture,
833 729
                        link.dui,
834 730
                        historical=True,
......
968 864

  
969 865
        transaction_amount = invoice['amount']
970 866
        transaction_id = data['transaction_id']
971
        transaction_date = encode_datetime(data['transaction_date'])
867
        transaction_date = utils.encode_datetime(data['transaction_date'])
972 868
        post_data = {
973 869
            'IDFACTURE': int(invoice_id),
974 870
            'IDREGIEENCAISSEMENT': '',
passerelle/contrib/toulouse_axel/utils.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2020  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import datetime
18
import xml.etree.ElementTree as ET
19

  
20

  
21
boolean_type = {
22
    'oneOf': [
23
        {'type': 'boolean'},
24
        {
25
            'type': 'string',
26
            'pattern': '[Oo][Uu][Ii]|[Nn][Oo][Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0',
27
        }
28
    ]
29
}
30
datetime_type = {
31
    'type': 'string',
32
    'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}',
33
}
34
json_date_format = '%Y-%m-%d'
35
json_datetime_format = '%Y-%m-%dT%H:%M:%S'
36
xml_date_format = '%d/%m/%Y'
37
xml_datetime_format = '%d/%m/%Y %H:%M:%S'
38

  
39

  
40
def indent(tree, space="  ", level=0):
41
    # backport from Lib/xml/etree/ElementTree.py python 3.9
42
    if isinstance(tree, ET.ElementTree):
43
        tree = tree.getroot()
44
    if level < 0:
45
        raise ValueError("Initial indentation level must be >= 0, got {level}".format(level))
46
    if not len(tree):
47
        return
48

  
49
    # Reduce the memory consumption by reusing indentation strings.
50
    indentations = ["\n" + level * space]
51

  
52
    def _indent_children(elem, level):
53
        # Start a new indentation level for the first child.
54
        child_level = level + 1
55
        try:
56
            child_indentation = indentations[child_level]
57
        except IndexError:
58
            child_indentation = indentations[level] + space
59
            indentations.append(child_indentation)
60

  
61
        if not elem.text or not elem.text.strip():
62
            elem.text = child_indentation
63

  
64
        for child in elem:
65
            if len(child):
66
                _indent_children(child, child_level)
67
            if not child.tail or not child.tail.strip():
68
                child.tail = child_indentation
69

  
70
        # Dedent after the last child by overwriting the previous indentation.
71
        if not child.tail.strip():
72
            child.tail = indentations[level]
73

  
74
    _indent_children(tree, 0)
75

  
76

  
77
def encode_bool(obj):
78
    if obj is True or str(obj).lower() in ['true', 'oui', '1']:
79
        return 'OUI'
80
    if obj is False or str(obj).lower() in ['false', 'non', '0']:
81
        return 'NON'
82
    return obj
83

  
84

  
85
def encode_datetime(obj):
86
    try:
87
        return datetime.datetime.strptime(obj, json_datetime_format).strftime(xml_datetime_format)
88
    except ValueError:
89
        return obj
90

  
91

  
92
def normalize_invoice(invoice, dui, historical=False, vendor_base=None):
93
    vendor = vendor_base or {}
94
    vendor.update(invoice)
95
    invoice_id = '%s-%s' % (dui, invoice['IDFACTURE'])
96
    if historical:
97
        invoice_id = 'historical-%s' % invoice_id
98
    data = {
99
        'id': invoice_id,
100
        'display_id': str(invoice['IDFACTURE']),
101
        'label': invoice['LIBELLE'],
102
        'paid': False,
103
        'vendor': {'toulouse-axel': vendor},
104
    }
105
    if historical:
106
        data.update({
107
            'amount': 0,
108
            'total_amount': invoice['MONTANT'],
109
            'created': invoice['EMISSION'],
110
            'pay_limit_date': '',
111
            'online_payment': False,
112
            'has_pdf': invoice['IPDF'] == '1',
113
        })
114
    else:
115
        data.update({
116
            'amount': invoice['RESTEAPAYER'],
117
            'total_amount': invoice['MONTANTTOTAL'],
118
            'created': invoice['DATEEMISSION'],
119
            'pay_limit_date': invoice['DATEECHEANCE'],
120
            'has_pdf': invoice['EXISTEPDF'] == '1',
121
        })
122
        pay_limit_date = datetime.datetime.strptime(invoice['DATEECHEANCE'], '%Y-%m-%d').date()
123
        data['online_payment'] = data['amount'] > 0 and pay_limit_date >= datetime.date.today()
124
    return data
0
-