Passed
Pull Request — 2.x (#1960)
by Jordi
06:31
created

senaite.core.schema.addressfield   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 17
eloc 62
dl 0
loc 128
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A AddressField._validate() 0 4 1
A AddressField.get_empty_address() 0 11 1
A AddressField.is_multi_address() 0 4 1
A AddressField.get_address_types() 0 10 3
A AddressField.get() 0 19 4
A AddressField.__init__() 0 6 2
A AddressField.set() 0 15 2
A AddressField.to_list() 0 8 3
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2022 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import copy
22
import six
23
24
from senaite.core.schema.fields import BaseField
25
from senaite.core.schema.interfaces import IAddressField
26
from zope.interface import implementer
27
from zope.schema import List
28
from zope.schema.interfaces import IFromUnicode
29
30
NAIVE_ADDRESS = "naive"
31
PHYSICAL_ADDRESS = "physical"
32
POSTAL_ADDRESS = "postal"
33
BILLING_ADDRESS = "billing"
34
BUSINESS_ADDRESS = "business"
35
OTHER_ADDRESS = "other"
36
37
38
@implementer(IAddressField, IFromUnicode)
39
class AddressField(List, BaseField):
40
    """A field that handles a single or multiple physical addresses
41
    """
42
43
    def __init__(self, address_types=None, **kw):
44
        if address_types is None:
45
            # Single address
46
            address_types = (NAIVE_ADDRESS,)
47
        self.address_types = address_types
48
        super(AddressField, self).__init__(**kw)
49
50
    def get_address_types(self):
51
        """Returns a tuple with the types of address handled by this field
52
        (e.g. address, postal-address, etc.)
53
        """
54
        address_types = self.address_types
55
        if not address_types:
56
            address_types = ()
57
        elif isinstance(address_types, six.string_types):
58
            address_types = (address_types, )
59
        return address_types
60
61
    def is_multi_address(self):
62
        """Returns whether this field handles multiple addresses
63
        """
64
        return len(self.get_address_types()) > 1
65
66
    def to_list(self, value, filter_empty=True):
67
        """Ensure the value is a list
68
        """
69
        if not isinstance(value, list):
70
            value = [value]
71
        if filter_empty:
72
            value = filter(None, value)
73
        return value
74
75
    def set(self, object, value):
76
        """Set an address record or records
77
        :param object: the instance of the field
78
        :param value: dict with address information or list of dicts
79
        :type value: list/tuple/dict
80
        """
81
        # Value is a list of dicts
82
        value = self.to_list(value)
83
84
        # Bail out non-supported address types
85
        address_types = self.get_address_types()
86
        value = filter(lambda ad: ad["type"] in address_types, value)
87
88
        # Set the value
89
        super(AddressField, self).set(object, value)
90
91
    def get(self, object):
92
        """Returns the address records
93
        :param object: the instance of this field
94
        :returns: list of dicts with address information for each address type
95
        """
96
        addresses = super(AddressField, self).get(object) or []
97
98
        # Sort and extend with non-existing address types
99
        output = []
100
        for address_type in self.get_address_types():
101
            address = filter(lambda rec: rec["type"] == address_type, addresses)
0 ignored issues
show
introduced by
The variable address_type does not seem to be defined in case the for loop on line 100 is not entered. Are you sure this can never be the case?
Loading history...
102
            if address:
103
                address = copy.deepcopy(address[0])
104
            else:
105
                address = self.get_empty_address(address_type)
106
            output.append(address)
107
108
        # Sort address in same order as types
109
        return output
110
111
    def get_empty_address(self, address_type):
112
        """Returns a dict that represents an empty address for the given type
113
        """
114
        return {
115
            "type": address_type,
116
            "address": "",
117
            "zip": "",
118
            "city": "",
119
            "subdivision2": "",
120
            "subdivision1": "",
121
            "country": "",
122
        }
123
124
    def _validate(self, value):
125
        """Validator called on form submit
126
        """
127
        super(AddressField, self)._validate(value)
128