Passed
Pull Request — 2.x (#1961)
by Jordi
09:44
created

senaite.core.api.geo   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 36
eloc 85
dl 0
loc 174
rs 9.52
c 0
b 0
f 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
B get_country() 0 25 7
D get_subdivision() 0 49 13
A is_subdivision() 0 7 2
B get_subdivisions() 0 26 7
A is_country() 0 7 2
A get_countries() 0 7 2
A get_country_code() 0 14 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 pycountry
22
from six import string_types
23
24
_marker = object()
25
26
27
def get_countries():
28
    """Return the list of countries sorted by name ascending
29
    :return: list of countries sorted by name ascending
30
    :rtype: list of Country objects
31
    """
32
    countries = pycountry.countries
33
    return sorted(list(countries), key=lambda s: s.name)
34
35
36
def get_country(country_subdivision_term, default=_marker):
37
    """Returns the country object
38
    :param country_subdivision_term: country, subdivision object or search term
39
    :type country_subdivision_term: Country/Subdivision/string
40
    :returns: the country that matches with the parameter passed in
41
    :rtype: pycountry.db.Country
42
    """
43
    if is_country(country_subdivision_term):
44
        return country_subdivision_term
45
46
    if is_subdivision(country_subdivision_term):
47
        return get_country(country_subdivision_term.country_code)
48
49
    if not isinstance(country_subdivision_term, string_types):
50
        if default is _marker:
51
            raise TypeError("{} is not supported".format(
52
                repr(country_subdivision_term)))
53
        return default
54
55
    try:
56
        return pycountry.countries.lookup(country_subdivision_term)
57
    except LookupError as e:
58
        if default is _marker:
59
            raise ValueError(str(e))
60
        return default
61
62
63
def get_country_code(country_subdivision_term, default=_marker):
64
    """Returns the 2-character code (alpha2) of the country
65
    :param country_subdivision_term: country, subdivision object or search term
66
    :return: the 2-character (alpha2) code of the country
67
    :rtype: string
68
    """
69
    if is_country(country_subdivision_term):
70
        return country_subdivision_term.alpha_2
71
    if is_subdivision(country_subdivision_term):
72
        return country_subdivision_term.country_code
73
74
    # Try to resolve the country by term
75
    country = get_country(country_subdivision_term, default=default)
76
    return country.alpha_2
77
78
79
def get_subdivision(subdivision_or_term, parent=None, default=_marker):
80
    """Returns the Subdivision object
81
    :param subdivision_or_term: subdivision or search term
82
    :param subdivision_or_term: Subdivision/string
83
    :param parent: filter by parent subdivision or country
84
    :returns: the subdivision that matches with the parameter passed in
85
    :rtype: pycountry.db.Subdivision
86
    """
87
    if is_subdivision(subdivision_or_term):
88
        return subdivision_or_term
89
90
    if not isinstance(subdivision_or_term, string_types):
91
        if default is _marker:
92
            raise TypeError("{} is not supported".format(
93
                repr(subdivision_or_term)))
94
        return default
95
96
    if parent:
97
        # Search by parent
98
        def to_utf8(value):
99
            if isinstance(value, unicode):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
100
                return value.encode("utf-8")
101
            return value
102
103
        def is_match(subdivision):
104
            terms = [subdivision.name, subdivision.code]
105
            needle = to_utf8(subdivision_or_term)
106
            return needle in map(to_utf8, terms)
107
108
        subdivisions = get_subdivisions(parent, default=_marker)
109
        subdivisions = filter(lambda subdiv: is_match(subdiv), subdivisions)
0 ignored issues
show
introduced by
The variable is_match does not seem to be defined for all execution paths.
Loading history...
110
        if len(subdivisions) == 1:
111
            return subdivisions[0]
112
        elif len(subdivisions) > 1:
113
            if default is _marker:
114
                raise ValueError("More than one subdivision found")
115
            return default
116
        else:
117
            if default is _marker:
118
                raise ValueError("No subdivisions found")
119
            return None
120
121
    # Search directly by term
122
    try:
123
        return pycountry.subdivisions.lookup(subdivision_or_term)
124
    except LookupError as e:
125
        if default is _marker:
126
            raise ValueError(str(e))
127
        return default
128
129
130
def is_country(something):
131
    """Returns whether the value passed in is a country object
132
    """
133
    if not something:
134
        return False
135
    # pycountry generates the classes dynamically, we cannot use isinstance
136
    return "Country" in repr(type(something))
137
138
139
def is_subdivision(something):
140
    """Returns whether the value passed in is a subdivision object
141
    """
142
    if not something:
143
        return False
144
    # pycountry generates the classes dynamically, we cannot use isinstance
145
    return "Subdivision" in repr(type(something))
146
147
148
def get_subdivisions(country_subdivision_term, default=_marker):
149
    """Returns the first-level subdivisions of the country or subdivision,
150
    sorted by code ascending
151
    :param country_subdivision_term: country, subdivision object or search term
152
    :return: the list of first-level subdivisions of the subdivision/country
153
    :rtype: list of pycountry.db.Subdivision
154
    """
155
    try:
156
        country_code = get_country_code(country_subdivision_term)
157
    except (ValueError, TypeError) as e:
158
        if default is _marker:
159
            raise e
160
        return default
161
162
    # Exract the subdivisions
163
    subdivisions = pycountry.subdivisions.get(country_code=country_code)
164
165
    # Bail out those that are not first-level
166
    if is_subdivision(country_subdivision_term):
167
        code = country_subdivision_term.code
168
        subdivisions = filter(lambda sub: sub.parent_code == code, subdivisions)
0 ignored issues
show
introduced by
The variable code does not seem to be defined for all execution paths.
Loading history...
169
    else:
170
        subdivisions = filter(lambda sub: sub.parent_code is None, subdivisions)
171
172
    # Sort by code
173
    return sorted(subdivisions, key=lambda s: s.code)
174