Passed
Push — main ( fd93eb...601700 )
by Sat CFDI
05:07
created

satcfdi.transform.helpers.format_address_raw()   D

Complexity

Conditions 12

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12.0135

Importance

Changes 0
Metric Value
cc 12
eloc 22
nop 10
dl 0
loc 32
ccs 21
cts 22
cp 0.9545
crap 12.0135
rs 4.8
c 0
b 0
f 0

How to fix   Complexity    Many Parameters   

Complexity

Complex classes like satcfdi.transform.helpers.format_address_raw() 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.

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1 1
import logging
2 1
import os
3 1
import pickle
4 1
import sqlite3
5 1
from decimal import Decimal
6 1
from lxml import etree
7 1
from lxml.etree import QName
8
9 1
from .. import CFDIError
10 1
from ..models import Code
11 1
from ..utils import iterate
12
13 1
logger = logging.getLogger(__name__)
14 1
TEXT_KEY = "_text"
15 1
_ = iterate
16 1
current_dir = os.path.dirname(__file__)
17
18
19 1
class SchemaCollector:
20 1
    def __init__(self):
21 1
        self.nsmap = {}
22 1
        self.schemas = []
23 1
        self.base = set()
24
25 1
    def add_schema(self, schema):
26 1
        if schema not in self.schemas:
27 1
            self.schemas.append(schema)
28
29 1
    def add_map(self, tns_alias, tns):
30 1
        if tns_alias in self.nsmap:
31 1
            if not self.nsmap[tns_alias] == tns:
32
                raise CFDIError("TNS Alias already registered with a different alias")
33
        else:
34 1
            self.nsmap[tns_alias] = tns
35
36 1
    def add_base(self, base):
37 1
        self.base.add(base)
38
39
40 1
class Xint(int):
41
    """
42
    An int class that knows the original value that was used to create it
43
    """
44
45 1
    def __new__(cls, value: str):
46 1
        self = int.__new__(cls, value)
47 1
        self.value = value
48 1
        return self
49
50 1
    def __str__(self):
51 1
        return self.value
52
53
54 1
def fmt_decimal(d):
55 1
    if isinstance(d, Decimal):
56 1
        return d.__format__("f")
57 1
    return str(d)
58
59
60 1
def simple_element(_tag, nsmap=None, text=None):
61 1
    el = etree.Element(_tag, nsmap=nsmap)
62 1
    el.text = text
63 1
    return el
64
65
66 1
def default_objectify(cls, node):
67 1
    self = cls()
68 1
    self.tag = node.tag
69
70 1
    def add_attributes(n, target):
71 1
        for k, v in n.attrib.items():
72 1
            target[k] = v
73
74 1
        text = n.text
75 1
        if text:
76 1
            text = n.text.strip()
77 1
            if text:
78 1
                target[TEXT_KEY] = text
79
80 1
    def add_elements(n, target):
81 1
        for el in n:
82 1
            new = cls()
83 1
            name = QName(el.tag).localname
84 1
            add_attributes(el, target=new)
85 1
            add_elements(el, target=new)
86
87 1
            current = target.get(name)
88 1
            if current is None:
89 1
                target[name] = new
90
            elif isinstance(current, cls):
91
                target[name] = [current, new]
92
            else:
93
                current.append(new)
94
95 1
    add_attributes(node, target=self)
96 1
    add_elements(node, target=self)
97
98 1
    return self
99
100
101 1
def impuesto_index(attrib, attribute_name):
102 1
    impuesto = attrib[attribute_name]
103 1
    ext = attribute_name[8:]
104 1
    if tipo_factor := attrib.get('TipoFactor' + ext):
105 1
        impuesto += "|" + tipo_factor
106 1
        if tasa_cuota := attrib.get('TasaOCuota' + ext):
107 1
            impuesto += "|" + Decimal(tasa_cuota).__format__(".6f")
108
109 1
    return impuesto
110
111
112 1
db_file = os.path.join(current_dir, "catalogos.db")
113 1
conn = sqlite3.connect(db_file)
114 1
c = conn.cursor()
115
116
117 1
def select(catalog_name, key):
118 1
    c.execute(f"SELECT value FROM {catalog_name} WHERE key = ?", (pickle.dumps(key),))
119 1
    if ds := c.fetchone():
120 1
        return pickle.loads(ds[0])
121
122
123 1
def select_all(catalog_name):
124 1
    c.execute(f"SELECT key, value FROM {catalog_name}")
125 1
    return {pickle.loads(k): pickle.loads(v) for k, v in c.fetchall()}
126
127
128 1
def catalog_code(catalog_name, key, index=None):
129 1
    code = key
130 1
    if isinstance(key, tuple):
131 1
        code = key[0]
132
133 1
    if ds := select(catalog_name, key):
134 1
        if index is not None:
135 1
            ds = ds[index]
136 1
    return Code(code, ds)
137
138
    # else:
139
    #     logger.error("Key Not found: %s %s", catalog_name, " ".join(args))
140
141
142 1
def moneda_decimales(moneda):
143 1
    return select('Tae00f1168e4dd44ad14f604041a8e80bcade7279', moneda)[1]
144
145
146 1
def codigo_postal_uso_horario(codigo_postal):
147 1
    return select('T1c22cc9094f6f89d8589f52d827f368d767db6b0', codigo_postal)[4]
148
149
150 1
def strcode(data):
151 1
    if isinstance(data, Code):
152 1
        return data.code
153 1
    return data
154
155
156 1
def format_address_raw(calle, num_exterior, num_interior, referencia, colonia, municipio, localidad, estado, pais, codigo_postal):
157 1
    parts = []
158
159 1
    calle_num = None
160 1
    if calle or num_exterior or num_interior:
161 1
        if num_exterior:
162 1
            calle_num = f"{calle} #{num_exterior}"
163
        else:
164 1
            calle_num = f"{calle}"
165
166 1
        if num_interior:
167 1
            calle_num = f"{calle_num}, int. #{num_interior}"
168
169 1
    if colonia:
170 1
        if calle_num:
171 1
            parts.append(f"{calle_num}, {colonia}")
172
        else:
173
            parts.append(f"{colonia}")
174
175 1
    if referencia:
176 1
        parts.append(f"{referencia}")
177
178 1
    if localidad and localidad != municipio:
179 1
        parts.append(f"{localidad}")
180
181 1
    if municipio:
182 1
        parts.append(f"{municipio}, {estado} {codigo_postal}")
183
    else:
184 1
        parts.append(f"{estado} {codigo_postal}")
185
186 1
    parts.append(f"{pais}")
187 1
    return "\n".join(parts)
188
189
190 1
def format_address(k):
191 1
    return format_address_raw(
192
        calle=k["Calle"],
193
        num_exterior=k.get("NumeroExterior"),
194
        num_interior=k.get("NumeroInterior"),
195
        referencia=desc(k.get("Referencia")),
196
        colonia=desc(k.get("Colonia")),
197
        municipio=desc(k.get("Municipio")),
198
        localidad=desc(k.get("Localidad")),
199
        estado=desc(k["Estado"]),
200
        pais=desc(k["Pais"]),
201
        codigo_postal=k["CodigoPostal"]
202
    )
203
204
205 1
def split_at_upper(word: str):
206 1
    def split_at_upper_itr(word: str):
207 1
        piu = None
208 1
        for w in word:
209 1
            niu = w.isupper()
210 1
            if piu == False:
211 1
                if niu:
212 1
                    yield " "
213
214 1
            if piu is None:
215 1
                yield w.upper()
216
            else:
217 1
                yield w
218 1
            piu = niu
219
220 1
    return "".join(split_at_upper_itr(word))
221
222
223 1
def trans(k):
224 1
    c.execute(f"SELECT value FROM TTranslations WHERE key = ?", (k,))
225 1
    if res := c.fetchone():
226 1
        res = res[0]
227
228 1
    if res is None:
229 1
        res = split_at_upper(k)
230 1
    return res
231
232
233 1
def desc(s):
234 1
    if isinstance(s, Code):
235 1
        return s.description
236
    return s
237