Passed
Push — main ( 7ce78e...a1d4f9 )
by Sat CFDI
02:13
created

satdigitalinvoice.utils   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 88
dl 0
loc 124
rs 10
c 0
b 0
f 0

14 Functions

Rating   Name   Duplication   Size   Complexity  
A parse_date_period() 0 8 4
A parse_ym_date() 0 2 1
A to_uuid() 0 5 2
A try_parsing_date() 0 7 3
A find_best_match() 0 8 5
A clear_directory() 0 3 1
A add_file_handler() 0 10 1
A random_string() 0 3 1
A to_int() 0 5 2
A cert_info() 0 14 2
A convert_ans1_date() 0 2 1
A first_duplicate() 0 7 3
A months_between() 0 2 1
A load_certificate() 0 8 2
1
import logging
2
import os
3
import random
4
import shutil
5
from datetime import datetime
6
from uuid import UUID
7
8
from satcfdi import Signer, DatePeriod
9
10
11
def parse_date_period(periodo):
12
    fmt, period = try_parsing_date(periodo)
13
    if fmt == '%Y':
14
        return DatePeriod(period.year)
15
    if fmt == '%Y-%m':
16
        return DatePeriod(period.year, period.month)
17
    if fmt == '%Y-%m-%d':
18
        return DatePeriod(period.year, period.month, period.day)
19
20
21
def try_parsing_date(text):
22
    for fmt in ('%Y', '%Y-%m', '%Y-%m-%d'):
23
        try:
24
            return fmt, datetime.strptime(text, fmt)
25
        except ValueError:
26
            pass
27
    raise ValueError('no valid date format found')
28
29
30
def parse_ym_date(periodo):
31
    return try_parsing_date(periodo)[1]
32
33
34
def to_uuid(s):
35
    try:
36
        return UUID(s)
37
    except ValueError:
38
        return None
39
40
41
def to_int(s):
42
    try:
43
        return int(s)
44
    except ValueError:
45
        return None
46
47
48
def random_string():
49
    chars = "0123456789abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXZY"
50
    return "".join(random.choice(chars) for _ in range(32))
51
52
53
def add_file_handler():
54
    fh = logging.FileHandler(
55
        f'.data/errors.log',
56
        mode='a',
57
        encoding='utf-8',
58
    )
59
    fh.setLevel(logging.ERROR)
60
    formatter = logging.Formatter('%(asctime)s - %(message)s')
61
    fh.setFormatter(formatter)
62
    logging.root.addHandler(fh)
63
64
65
def convert_ans1_date(ans1_date):
66
    return datetime.strptime(ans1_date.decode('utf-8'), '%Y%m%d%H%M%SZ')
67
68
69
def cert_info(signer: Signer):
70
    if signer:
71
        return {
72
            "NoCertificado": signer.certificate_number,
73
            "Tipo": str(signer.type),
74
75
            "organizationName": signer.certificate.get_subject().O,
76
            "x500UniqueIdentifier": signer.certificate.get_subject().x500UniqueIdentifier,
77
            "serialNumber": signer.certificate.get_subject().serialNumber,
78
            "organizationUnitName": signer.certificate.get_subject().OU,
79
            "emailAddress": signer.certificate.get_subject().emailAddress,
80
81
            "notAfter": convert_ans1_date(signer.certificate.get_notAfter()),
82
            "notBefore": convert_ans1_date(signer.certificate.get_notBefore()),
83
        }
84
85
86
def find_best_match(cases, emission_date):
87
    fk, fv = (None, None)
88
    for k, v in cases.items():
89
        k = parse_ym_date(k)
90
        if k <= emission_date:
91
            if fk is None or k > fk:
92
                fk, fv = k, v
93
    return fk, fv
94
95
96
def clear_directory(directory):
97
    shutil.rmtree(directory, ignore_errors=True)
98
    os.makedirs(directory, exist_ok=True)
99
100
101
# calculate the number of months between two dates
102
def months_between(d1, d2):
103
    return (d1.year - d2.year) * 12 + d1.month - d2.month
104
105
106
def load_certificate(data):
107
    if 'data' in data:
108
        return Signer.load_pkcs12(
109
            **data,
110
        )
111
    else:
112
        return Signer.load(
113
            **data,
114
        )
115
116
117
def first_duplicate(seq):
118
    seen = set()
119
    for x in seq:
120
        if x in seen:
121
            return x
122
        seen.add(x)
123
    return None
124