TelefoonSchemaNode.validator()   C
last analyzed

Complexity

Conditions 10

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 10
c 2
b 0
f 1
dl 0
loc 22
rs 5.9999

How to fix   Complexity   

Complexity

Complex classes like TelefoonSchemaNode.validator() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
'''
3
This module allows for the validation of actor data like national ID number and telephone.
4
'''
5
6
import colander
7
import re
8
9
10
def is_number(s):
11
    try:
12
        int(s)
13
    except ValueError:
14
        return False
15
    else:
16
        return True
17
18
19
class RRNSchemaNode(colander.SchemaNode):
20
    title = 'rrn'
21
    schema_type = colander.String
22
23
    @staticmethod
24
    def validator(node, rrn):
25
        '''
26
        Colander validator that checks whether a given value is a valid
27
        Belgian national ID number .
28
        :raises colander.Invalid: If the value is no valid Belgian national ID number.
29
        '''
30
31
        def _valid(_rrn):
32
            x = 97 - (int(_rrn[:-2]) - (int(_rrn[:-2]) // 97) * 97)
33
            return int(_rrn[-2:]) == x
34
35
        if len(rrn) != 11:
36
            raise colander.Invalid(
37
                node,
38
                'Een rijksregisternummer moet 11 cijfers lang zijn.'
39
            )
40
        elif not _valid(rrn):
41
            valid_rrn = False
42
            if rrn[:1] == '0' or rrn[:1] == '1':
43
                rrn = '2' + rrn
44
                valid_rrn = _valid(rrn)
45
            if not valid_rrn:
46
                raise colander.Invalid(
47
                    node,
48
                    'Dit is geen correct rijksregisternummer.'
49
                )
50
51
52
class KBOSchemaNode(colander.SchemaNode):
53
    title = 'kbo'
54
    schema_type = colander.String
55
56
    @staticmethod
57
    def preparer(value):
58
        '''
59
        Edit a value to a value that can be validated as a
60
        kbo number.
61
        '''
62
        if value is None or value == colander.null:
63
            return colander.null
64
        return value.strip().replace('.', '')
65
66
    @staticmethod
67
    def validator(node, value):
68
        '''
69
        Colander validator that checks whether a given value is a valid
70
        is company number.
71
        For our purposes , we expect a firm number that
72
        is composed of nine or ten characters like 2028445291.
73
        Sometimes a company number is formatted with separation marks like 0.400.378.485.
74
        Therefore, there is also a : Func: ` actoren.validators.kbo_preparer` which transforms such input.
75
        :raises colander.Invalid: if the value is valid Belgian company number.
76
        '''
77
78
        if not re.match(r'^[0-9]{9,10}$', value):
79
            raise colander.Invalid(
80
                node,
81
                'Dit is geen correct ondernemingsnummer.'
82
            )
83
84
85
class TelefoonSchemaNode(colander.MappingSchema):
86
    landcode = colander.SchemaNode(
87
        colander.String(),
88
        missing="+32"
89
    )
90
    nummer = colander.SchemaNode(
91
        colander.String(),
92
        missing=""
93
    )
94
95
    @staticmethod
96
    def preparer(telefoon):
97
        '''
98
        Edit a phone value to a value that can be validated as a
99
        phone number.
100
        This takes the incoming value and :
101
            Removes all whitespace ( space, tab , newline , ... ) characters
102
            Removes the following characters: " / - . "
103
            If no + is present at frond, add the country code
104
            In short: just add a + at the beginning of the country code.
105
        '''
106
        if telefoon is None or telefoon == colander.null:
107
            return colander.null
108
        if 'landcode' in telefoon and telefoon.get('landcode') is not None:
109
            landcode = telefoon.get('landcode')
110
            value = re.sub(r'\s+', '', landcode).replace('.', '').replace('/', '').replace(',', '').replace('-', ''). \
111
                lstrip('0')
112
            telefoon['landcode'] = '+' + value if value[0] != '+' else value
113
        if 'nummer' in telefoon and telefoon.get('nummer') is not None:
114
            nummer = telefoon.get('nummer')
115
            value = re.sub(r'\s+', '', nummer).replace('.', '').replace('/', '').replace(',', '').replace('-', ''). \
116
                lstrip('0')
117
            telefoon['nummer'] = value
118
        return telefoon
119
120
    @staticmethod
121
    def validator(node, telefoon):
122
        ''' A valid international phone number looks like this: + .
123
        It is up to 15 digits long . The country code consists of 1 to 4 digits.
124
        The actual number may even 15 - cells ( country code) figures cover . We're going to keep the phone numbers including a + to indicate the international call prefix.
125
        A valid number always begins with a + . The shortest valid number that we want to keep is: +11 ( 3 characters). The longest : +123123456789123 (16 characters).
126
        Additional validation (after preparation ) : If a number starts with +32 (this is a Belgian number) , it must then follow eight or nine digits . The first decimal after +32 may not be 0 .
127
        '''
128
        if telefoon.get('nummer', '') != "":
129
            msg = "Invalid phone number"
130
            landcode = telefoon.get('landcode', '')
131
            nummer = telefoon.get('nummer', '')
132
            if landcode[0] != '+' or len(landcode[1:]) > 4 or not is_number(landcode[1:]):
133
                raise colander.Invalid(node,
134
                                       msg + ': Een geldige landcode begint met een + gevolgd door maximaal 4 cijfers')
135
            if landcode[0] != '+' or len(landcode[1:]) + len(nummer) > 15 or not is_number(nummer):
136
                raise colander.Invalid(node,
137
                                       msg + ': Een geldige nummer begint met een + gevolgd door maximaal 15 cijfers')
138
            # validatie van Belgische telefoonnummers
139
            if landcode == '+32':
140
                if len(nummer) not in [8, 9]:
141
                    raise colander.Invalid(node, msg + ': Na +32 moeten er 8 of 9 cijfers volgen')
142