catalogmx 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- catalogmx/__init__.py +56 -0
- catalogmx/catalogs/__init__.py +5 -0
- catalogmx/catalogs/banxico/__init__.py +24 -0
- catalogmx/catalogs/banxico/banks.py +136 -0
- catalogmx/catalogs/banxico/codigos_plaza.py +287 -0
- catalogmx/catalogs/banxico/instituciones_financieras.py +338 -0
- catalogmx/catalogs/banxico/monedas_divisas.py +386 -0
- catalogmx/catalogs/banxico/udis.py +279 -0
- catalogmx/catalogs/ift/__init__.py +15 -0
- catalogmx/catalogs/ift/codigos_lada.py +426 -0
- catalogmx/catalogs/ift/operadores_moviles.py +315 -0
- catalogmx/catalogs/inegi/__init__.py +21 -0
- catalogmx/catalogs/inegi/localidades.py +207 -0
- catalogmx/catalogs/inegi/municipios.py +73 -0
- catalogmx/catalogs/inegi/municipios_completo.py +236 -0
- catalogmx/catalogs/inegi/states.py +148 -0
- catalogmx/catalogs/mexico/__init__.py +17 -0
- catalogmx/catalogs/mexico/hoy_no_circula.py +215 -0
- catalogmx/catalogs/mexico/placas_formatos.py +184 -0
- catalogmx/catalogs/mexico/salarios_minimos.py +156 -0
- catalogmx/catalogs/mexico/uma.py +207 -0
- catalogmx/catalogs/sat/__init__.py +13 -0
- catalogmx/catalogs/sat/carta_porte/__init__.py +19 -0
- catalogmx/catalogs/sat/carta_porte/aeropuertos.py +76 -0
- catalogmx/catalogs/sat/carta_porte/carreteras.py +59 -0
- catalogmx/catalogs/sat/carta_porte/config_autotransporte.py +54 -0
- catalogmx/catalogs/sat/carta_porte/material_peligroso.py +66 -0
- catalogmx/catalogs/sat/carta_porte/puertos_maritimos.py +63 -0
- catalogmx/catalogs/sat/carta_porte/tipo_embalaje.py +48 -0
- catalogmx/catalogs/sat/carta_porte/tipo_permiso.py +54 -0
- catalogmx/catalogs/sat/cfdi_4/__init__.py +42 -0
- catalogmx/catalogs/sat/cfdi_4/clave_prod_serv.py +383 -0
- catalogmx/catalogs/sat/cfdi_4/clave_unidad.py +298 -0
- catalogmx/catalogs/sat/cfdi_4/exportacion.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/forma_pago.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/impuesto.py +57 -0
- catalogmx/catalogs/sat/cfdi_4/meses.py +34 -0
- catalogmx/catalogs/sat/cfdi_4/metodo_pago.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/objeto_imp.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/periodicidad.py +34 -0
- catalogmx/catalogs/sat/cfdi_4/regimen_fiscal.py +57 -0
- catalogmx/catalogs/sat/cfdi_4/tasa_o_cuota.py +42 -0
- catalogmx/catalogs/sat/cfdi_4/tipo_comprobante.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/tipo_factor.py +34 -0
- catalogmx/catalogs/sat/cfdi_4/tipo_relacion.py +45 -0
- catalogmx/catalogs/sat/cfdi_4/uso_cfdi.py +45 -0
- catalogmx/catalogs/sat/comercio_exterior/__init__.py +39 -0
- catalogmx/catalogs/sat/comercio_exterior/claves_pedimento.py +77 -0
- catalogmx/catalogs/sat/comercio_exterior/estados.py +122 -0
- catalogmx/catalogs/sat/comercio_exterior/incoterms.py +226 -0
- catalogmx/catalogs/sat/comercio_exterior/monedas.py +107 -0
- catalogmx/catalogs/sat/comercio_exterior/motivos_traslado.py +54 -0
- catalogmx/catalogs/sat/comercio_exterior/paises.py +88 -0
- catalogmx/catalogs/sat/comercio_exterior/registro_ident_trib.py +76 -0
- catalogmx/catalogs/sat/comercio_exterior/unidades_aduana.py +54 -0
- catalogmx/catalogs/sat/comercio_exterior/validator.py +212 -0
- catalogmx/catalogs/sat/nomina/__init__.py +19 -0
- catalogmx/catalogs/sat/nomina/banco.py +50 -0
- catalogmx/catalogs/sat/nomina/periodicidad_pago.py +48 -0
- catalogmx/catalogs/sat/nomina/riesgo_puesto.py +56 -0
- catalogmx/catalogs/sat/nomina/tipo_contrato.py +47 -0
- catalogmx/catalogs/sat/nomina/tipo_jornada.py +42 -0
- catalogmx/catalogs/sat/nomina/tipo_nomina.py +52 -0
- catalogmx/catalogs/sat/nomina/tipo_regimen.py +47 -0
- catalogmx/catalogs/sepomex/__init__.py +5 -0
- catalogmx/catalogs/sepomex/codigos_postales.py +184 -0
- catalogmx/cli.py +185 -0
- catalogmx/helpers.py +324 -0
- catalogmx/utils/text.py +55 -0
- catalogmx/validators/__init__.py +0 -0
- catalogmx/validators/clabe.py +233 -0
- catalogmx/validators/curp.py +623 -0
- catalogmx/validators/nss.py +255 -0
- catalogmx/validators/rfc.py +1004 -0
- catalogmx-0.3.0.dist-info/METADATA +644 -0
- catalogmx-0.3.0.dist-info/RECORD +81 -0
- catalogmx-0.3.0.dist-info/WHEEL +5 -0
- catalogmx-0.3.0.dist-info/entry_points.txt +2 -0
- catalogmx-0.3.0.dist-info/licenses/AUTHORS.rst +5 -0
- catalogmx-0.3.0.dist-info/licenses/LICENSE +19 -0
- catalogmx-0.3.0.dist-info/top_level.txt +1 -0
catalogmx/__init__.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
2
|
+
|
|
3
|
+
# RFC imports
|
|
4
|
+
# Modern helper functions (recommended API)
|
|
5
|
+
from .helpers import (
|
|
6
|
+
detect_rfc_type,
|
|
7
|
+
# CURP helpers
|
|
8
|
+
generate_curp,
|
|
9
|
+
# RFC helpers
|
|
10
|
+
generate_rfc_persona_fisica,
|
|
11
|
+
generate_rfc_persona_moral,
|
|
12
|
+
get_curp_info,
|
|
13
|
+
is_valid_curp,
|
|
14
|
+
is_valid_rfc,
|
|
15
|
+
validate_curp,
|
|
16
|
+
validate_rfc,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# CURP imports
|
|
20
|
+
from .validators.curp import (
|
|
21
|
+
CURPException,
|
|
22
|
+
CURPGenerator,
|
|
23
|
+
CURPLengthError,
|
|
24
|
+
CURPStructureError,
|
|
25
|
+
CURPValidator,
|
|
26
|
+
)
|
|
27
|
+
from .validators.rfc import (
|
|
28
|
+
RFCGenerator,
|
|
29
|
+
RFCGeneratorFisicas,
|
|
30
|
+
RFCGeneratorMorales,
|
|
31
|
+
RFCValidator,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# RFC Classes (legacy/advanced usage)
|
|
36
|
+
"RFCValidator",
|
|
37
|
+
"RFCGenerator",
|
|
38
|
+
"RFCGeneratorFisicas",
|
|
39
|
+
"RFCGeneratorMorales",
|
|
40
|
+
# CURP Classes (legacy/advanced usage)
|
|
41
|
+
"CURPValidator",
|
|
42
|
+
"CURPGenerator",
|
|
43
|
+
"CURPException",
|
|
44
|
+
"CURPLengthError",
|
|
45
|
+
"CURPStructureError",
|
|
46
|
+
# Modern helper functions (recommended)
|
|
47
|
+
"generate_rfc_persona_fisica",
|
|
48
|
+
"generate_rfc_persona_moral",
|
|
49
|
+
"validate_rfc",
|
|
50
|
+
"detect_rfc_type",
|
|
51
|
+
"is_valid_rfc",
|
|
52
|
+
"generate_curp",
|
|
53
|
+
"validate_curp",
|
|
54
|
+
"get_curp_info",
|
|
55
|
+
"is_valid_curp",
|
|
56
|
+
]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
catalogmx.catalogs.banxico - Catálogos de Banxico
|
|
3
|
+
|
|
4
|
+
Catálogos incluidos:
|
|
5
|
+
- BankCatalog: Bancos autorizados por Banxico
|
|
6
|
+
- UDICatalog: UDIs (Unidades de Inversión)
|
|
7
|
+
- InstitucionesFinancieras: Tipos de instituciones del sistema financiero
|
|
8
|
+
- MonedasDivisas: Monedas y divisas internacionales
|
|
9
|
+
- CodigosPlazaCatalog: Códigos de plaza para CLABE
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .banks import BankCatalog
|
|
13
|
+
from .codigos_plaza import CodigosPlazaCatalog
|
|
14
|
+
from .instituciones_financieras import InstitucionesFinancieras
|
|
15
|
+
from .monedas_divisas import MonedasDivisas
|
|
16
|
+
from .udis import UDICatalog
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BankCatalog",
|
|
20
|
+
"UDICatalog",
|
|
21
|
+
"InstitucionesFinancieras",
|
|
22
|
+
"MonedasDivisas",
|
|
23
|
+
"CodigosPlazaCatalog",
|
|
24
|
+
]
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bank Catalog from Banxico
|
|
3
|
+
|
|
4
|
+
This module provides access to the official catalog of Mexican banks
|
|
5
|
+
participating in the SPEI (Sistema de Pagos Electrónicos Interbancarios).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from catalogmx.utils.text import normalize_text
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BankCatalog:
|
|
15
|
+
"""
|
|
16
|
+
Catalog of Mexican banks
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_data: list[dict] | None = None
|
|
20
|
+
_bank_by_code: dict[str, dict] | None = None
|
|
21
|
+
_bank_by_name: dict[str, dict] | None = None
|
|
22
|
+
_bank_by_name_normalized: dict[str, dict] | None = None
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def _load_data(cls) -> None:
|
|
26
|
+
"""Load bank data from JSON file"""
|
|
27
|
+
if cls._data is None:
|
|
28
|
+
# Path: catalogmx/packages/python/catalogmx/catalogs/banxico/banks.py
|
|
29
|
+
# Target: catalogmx/packages/shared-data/banxico/banks.json
|
|
30
|
+
current_file = Path(__file__)
|
|
31
|
+
shared_data_path = (
|
|
32
|
+
current_file.parent.parent.parent.parent.parent
|
|
33
|
+
/ "shared-data"
|
|
34
|
+
/ "banxico"
|
|
35
|
+
/ "banks.json"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
with open(shared_data_path, encoding="utf-8") as f:
|
|
39
|
+
cls._data = json.load(f)
|
|
40
|
+
|
|
41
|
+
# Create lookup dictionaries
|
|
42
|
+
cls._bank_by_code = {bank["code"]: bank for bank in cls._data}
|
|
43
|
+
cls._bank_by_name = {bank["name"].upper(): bank for bank in cls._data}
|
|
44
|
+
# Accent-insensitive lookup
|
|
45
|
+
cls._bank_by_name_normalized = {
|
|
46
|
+
normalize_text(bank["name"]): bank for bank in cls._data
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def get_all_banks(cls) -> list[dict]:
|
|
51
|
+
"""
|
|
52
|
+
Get all banks in the catalog
|
|
53
|
+
|
|
54
|
+
:return: List of bank dictionaries
|
|
55
|
+
"""
|
|
56
|
+
cls._load_data()
|
|
57
|
+
return cls._data.copy()
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def get_bank_by_code(cls, code: str) -> dict | None:
|
|
61
|
+
"""
|
|
62
|
+
Get bank information by code
|
|
63
|
+
|
|
64
|
+
:param code: 3-digit bank code (e.g., '002' for Banamex)
|
|
65
|
+
:return: Bank dictionary or None if not found
|
|
66
|
+
"""
|
|
67
|
+
cls._load_data()
|
|
68
|
+
code = str(code).zfill(3)
|
|
69
|
+
return cls._bank_by_code.get(code)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def get_bank_by_name(cls, name: str) -> dict | None:
|
|
73
|
+
"""
|
|
74
|
+
Get bank information by name (accent-insensitive)
|
|
75
|
+
|
|
76
|
+
:param name: Bank name (case and accent insensitive, e.g., 'BANAMEX' or 'Banamex')
|
|
77
|
+
:return: Bank dictionary or None if not found
|
|
78
|
+
|
|
79
|
+
Examples:
|
|
80
|
+
>>> # Both searches work the same
|
|
81
|
+
>>> bank = BankCatalog.get_bank_by_name("Banamex")
|
|
82
|
+
>>> bank = BankCatalog.get_bank_by_name("Banámex") # same result
|
|
83
|
+
"""
|
|
84
|
+
cls._load_data()
|
|
85
|
+
return cls._bank_by_name_normalized.get(normalize_text(name))
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def is_spei_participant(cls, code: str) -> bool:
|
|
89
|
+
"""
|
|
90
|
+
Check if a bank participates in SPEI
|
|
91
|
+
|
|
92
|
+
:param code: 3-digit bank code
|
|
93
|
+
:return: True if bank participates in SPEI, False otherwise
|
|
94
|
+
"""
|
|
95
|
+
bank = cls.get_bank_by_code(code)
|
|
96
|
+
return bank.get("spei", False) if bank else False
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def get_spei_banks(cls) -> list[dict]:
|
|
100
|
+
"""
|
|
101
|
+
Get all banks that participate in SPEI
|
|
102
|
+
|
|
103
|
+
:return: List of SPEI participant banks
|
|
104
|
+
"""
|
|
105
|
+
cls._load_data()
|
|
106
|
+
return [bank for bank in cls._data if bank.get("spei", False)]
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def validate_bank_code(cls, code: str) -> bool:
|
|
110
|
+
"""
|
|
111
|
+
Validate if a bank code exists
|
|
112
|
+
|
|
113
|
+
:param code: 3-digit bank code
|
|
114
|
+
:return: True if exists, False otherwise
|
|
115
|
+
"""
|
|
116
|
+
return cls.get_bank_by_code(code) is not None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Convenience dictionaries for direct access
|
|
120
|
+
def get_banks_dict() -> dict[str, dict]:
|
|
121
|
+
"""Get dictionary of all banks indexed by code"""
|
|
122
|
+
BankCatalog._load_data()
|
|
123
|
+
return BankCatalog._bank_by_code.copy()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_spei_banks() -> list[dict]:
|
|
127
|
+
"""Get list of all SPEI participant banks"""
|
|
128
|
+
return BankCatalog.get_spei_banks()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Export commonly used functions
|
|
132
|
+
__all__ = [
|
|
133
|
+
"BankCatalog",
|
|
134
|
+
"get_banks_dict",
|
|
135
|
+
"get_spei_banks",
|
|
136
|
+
]
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Catálogo de Códigos de Plaza CLABE
|
|
3
|
+
=====================================
|
|
4
|
+
|
|
5
|
+
Códigos de plaza para el sistema CLABE (Clave Bancaria Estandarizada).
|
|
6
|
+
Los códigos de plaza son identificadores de 3 dígitos que indican la ubicación
|
|
7
|
+
geográfica de las sucursales bancarias en México.
|
|
8
|
+
|
|
9
|
+
Fuente: Banco de México (BANXICO) / Sistema de Pagos Electrónicos Interbancarios (SPEI)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
from typing import TypedDict
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from unidecode import unidecode
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Fallback if unidecode not available
|
|
20
|
+
def unidecode(text):
|
|
21
|
+
return text
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CodigoPlaza(TypedDict):
|
|
25
|
+
"""Estructura de un código de plaza CLABE."""
|
|
26
|
+
|
|
27
|
+
codigo: str # Código de 3 dígitos
|
|
28
|
+
plaza: str # Nombre de la plaza/ciudad
|
|
29
|
+
estado: str # Estado
|
|
30
|
+
cve_entidad: str # Código INEGI del estado
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CodigosPlazaCatalog:
|
|
34
|
+
"""Catálogo de códigos de plaza para CLABE."""
|
|
35
|
+
|
|
36
|
+
_data: list[CodigoPlaza] | None = None
|
|
37
|
+
_by_codigo: dict[str, list[CodigoPlaza]] | None = None
|
|
38
|
+
_by_estado: dict[str, list[CodigoPlaza]] | None = None
|
|
39
|
+
_by_plaza: dict[str, list[CodigoPlaza]] | None = None
|
|
40
|
+
_by_plaza_normalized: dict[str, list[CodigoPlaza]] | None = None
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def _normalize(cls, text: str) -> str:
|
|
44
|
+
"""Normaliza texto removiendo acentos y convirtiendo a mayúsculas."""
|
|
45
|
+
return unidecode(text).upper()
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def _load(cls) -> None:
|
|
49
|
+
"""Carga los datos del catálogo."""
|
|
50
|
+
if cls._data is not None:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
data_path = os.path.join(
|
|
54
|
+
os.path.dirname(__file__), "../../../../shared-data/banxico/codigos_plaza.json"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
with open(data_path, encoding="utf-8") as f:
|
|
58
|
+
catalog = json.load(f)
|
|
59
|
+
cls._data = catalog["plazas"]
|
|
60
|
+
|
|
61
|
+
# Build indices
|
|
62
|
+
cls._by_codigo = {}
|
|
63
|
+
cls._by_estado = {}
|
|
64
|
+
cls._by_plaza = {}
|
|
65
|
+
cls._by_plaza_normalized = {}
|
|
66
|
+
|
|
67
|
+
for plaza in cls._data:
|
|
68
|
+
# By codigo (puede haber múltiples plazas con el mismo código)
|
|
69
|
+
if plaza["codigo"] not in cls._by_codigo:
|
|
70
|
+
cls._by_codigo[plaza["codigo"]] = []
|
|
71
|
+
cls._by_codigo[plaza["codigo"]].append(plaza)
|
|
72
|
+
|
|
73
|
+
# By estado
|
|
74
|
+
if plaza["estado"] not in cls._by_estado:
|
|
75
|
+
cls._by_estado[plaza["estado"]] = []
|
|
76
|
+
cls._by_estado[plaza["estado"]].append(plaza)
|
|
77
|
+
|
|
78
|
+
# By plaza name (exact match)
|
|
79
|
+
plaza_key = plaza["plaza"].upper()
|
|
80
|
+
if plaza_key not in cls._by_plaza:
|
|
81
|
+
cls._by_plaza[plaza_key] = []
|
|
82
|
+
cls._by_plaza[plaza_key].append(plaza)
|
|
83
|
+
|
|
84
|
+
# By plaza name (normalized, accent-insensitive)
|
|
85
|
+
plaza_normalized = cls._normalize(plaza["plaza"])
|
|
86
|
+
if plaza_normalized not in cls._by_plaza_normalized:
|
|
87
|
+
cls._by_plaza_normalized[plaza_normalized] = []
|
|
88
|
+
cls._by_plaza_normalized[plaza_normalized].append(plaza)
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def get_all(cls) -> list[CodigoPlaza]:
|
|
92
|
+
"""
|
|
93
|
+
Obtiene todos los códigos de plaza.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Lista con todos los códigos de plaza
|
|
97
|
+
"""
|
|
98
|
+
cls._load()
|
|
99
|
+
return cls._data.copy()
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def buscar_por_codigo(cls, codigo: str) -> list[CodigoPlaza]:
|
|
103
|
+
"""
|
|
104
|
+
Busca plazas por código.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
codigo: Código de plaza (3 dígitos)
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Lista de plazas con ese código (puede haber múltiples)
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
>>> plazas = CodigosPlazaCatalog.buscar_por_codigo("320")
|
|
114
|
+
>>> for p in plazas:
|
|
115
|
+
... print(f"{p['plaza']}, {p['estado']}")
|
|
116
|
+
Guadalajara, Jalisco
|
|
117
|
+
Tonala, Jalisco
|
|
118
|
+
...
|
|
119
|
+
"""
|
|
120
|
+
cls._load()
|
|
121
|
+
codigo_padded = codigo.zfill(3)
|
|
122
|
+
return cls._by_codigo.get(codigo_padded, [])
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def buscar_por_plaza(cls, nombre_plaza: str) -> list[CodigoPlaza]:
|
|
126
|
+
"""
|
|
127
|
+
Busca códigos por nombre de plaza (insensible a acentos).
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
nombre_plaza: Nombre de la plaza/ciudad
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Lista de códigos para esa plaza
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> # Tonalá aparece en dos estados diferentes
|
|
137
|
+
>>> plazas = CodigosPlazaCatalog.buscar_por_plaza("Tonala")
|
|
138
|
+
>>> for p in plazas:
|
|
139
|
+
... print(f"Código {p['codigo']}: {p['plaza']}, {p['estado']}")
|
|
140
|
+
Código 135: Tonala, Chiapas
|
|
141
|
+
Código 320: Tonala, Jalisco
|
|
142
|
+
|
|
143
|
+
>>> # Búsqueda insensible a acentos
|
|
144
|
+
>>> tuxpam = CodigosPlazaCatalog.buscar_por_plaza("Tuxpam") # sin acento
|
|
145
|
+
>>> print(len(tuxpam)) # Encuentra "Túxpam" con acento
|
|
146
|
+
2
|
|
147
|
+
"""
|
|
148
|
+
cls._load()
|
|
149
|
+
plaza_normalized = cls._normalize(nombre_plaza)
|
|
150
|
+
return cls._by_plaza_normalized.get(plaza_normalized, [])
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def get_por_estado(cls, estado: str) -> list[CodigoPlaza]:
|
|
154
|
+
"""
|
|
155
|
+
Obtiene todas las plazas de un estado.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
estado: Nombre del estado
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Lista de plazas en ese estado
|
|
162
|
+
|
|
163
|
+
Examples:
|
|
164
|
+
>>> plazas = CodigosPlazaCatalog.get_por_estado("Jalisco")
|
|
165
|
+
>>> print(f"Jalisco tiene {len(plazas)} plazas")
|
|
166
|
+
"""
|
|
167
|
+
cls._load()
|
|
168
|
+
return cls._by_estado.get(estado, [])
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def get_por_cve_entidad(cls, cve_entidad: str) -> list[CodigoPlaza]:
|
|
172
|
+
"""
|
|
173
|
+
Obtiene todas las plazas por código INEGI de entidad.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
cve_entidad: Código INEGI del estado (2 dígitos)
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Lista de plazas en esa entidad
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
>>> # Jalisco tiene cve_entidad '14'
|
|
183
|
+
>>> plazas = CodigosPlazaCatalog.get_por_cve_entidad("14")
|
|
184
|
+
>>> print(f"Entidad 14 tiene {len(plazas)} plazas")
|
|
185
|
+
"""
|
|
186
|
+
cls._load()
|
|
187
|
+
return [p for p in cls._data if p["cve_entidad"] == cve_entidad]
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def validar_codigo_clabe(cls, codigo_plaza: str) -> dict:
|
|
191
|
+
"""
|
|
192
|
+
Valida un código de plaza dentro de una CLABE.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
codigo_plaza: Código de plaza (3 dígitos)
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Diccionario con información de validación
|
|
199
|
+
|
|
200
|
+
Examples:
|
|
201
|
+
>>> result = CodigosPlazaCatalog.validar_codigo_clabe("180")
|
|
202
|
+
>>> print(result['valido'])
|
|
203
|
+
True
|
|
204
|
+
>>> print(result['plazas'][0]['plaza'])
|
|
205
|
+
Ciudad de México
|
|
206
|
+
"""
|
|
207
|
+
cls._load()
|
|
208
|
+
codigo_padded = codigo_plaza.zfill(3)
|
|
209
|
+
plazas = cls.buscar_por_codigo(codigo_padded)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
"valido": len(plazas) > 0,
|
|
213
|
+
"codigo": codigo_padded,
|
|
214
|
+
"plazas": plazas,
|
|
215
|
+
"num_plazas": len(plazas),
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@classmethod
|
|
219
|
+
def get_plazas_duplicadas(cls) -> dict[str, list[CodigoPlaza]]:
|
|
220
|
+
"""
|
|
221
|
+
Obtiene plazas con nombres duplicados en diferentes estados.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Diccionario con nombres de plaza y sus instancias
|
|
225
|
+
|
|
226
|
+
Examples:
|
|
227
|
+
>>> duplicadas = CodigosPlazaCatalog.get_plazas_duplicadas()
|
|
228
|
+
>>> for nombre, plazas in duplicadas.items():
|
|
229
|
+
... print(f"{nombre}: {len(plazas)} ubicaciones")
|
|
230
|
+
Tonala: 2 ubicaciones (Chiapas, Jalisco)
|
|
231
|
+
Túxpam: 2 ubicaciones (Jalisco, Nayarit)
|
|
232
|
+
"""
|
|
233
|
+
cls._load()
|
|
234
|
+
duplicadas = {}
|
|
235
|
+
for nombre, plazas in cls._by_plaza.items():
|
|
236
|
+
if len(plazas) > 1:
|
|
237
|
+
duplicadas[nombre] = plazas
|
|
238
|
+
return duplicadas
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def search(cls, query: str) -> list[CodigoPlaza]:
|
|
242
|
+
"""
|
|
243
|
+
Busca plazas por nombre parcial (insensible a acentos y mayúsculas).
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
query: Texto a buscar
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Lista de plazas que coinciden
|
|
250
|
+
|
|
251
|
+
Examples:
|
|
252
|
+
>>> # Buscar todas las plazas con "San" en el nombre
|
|
253
|
+
>>> plazas = CodigosPlazaCatalog.search("San")
|
|
254
|
+
>>> for p in plazas[:5]:
|
|
255
|
+
... print(f"{p['codigo']}: {p['plaza']}, {p['estado']}")
|
|
256
|
+
|
|
257
|
+
>>> # Búsqueda insensible a acentos
|
|
258
|
+
>>> plazas = CodigosPlazaCatalog.search("Tuxpam") # sin acento
|
|
259
|
+
>>> print(len(plazas)) # Encuentra "Túxpam" con acento
|
|
260
|
+
3
|
|
261
|
+
"""
|
|
262
|
+
cls._load()
|
|
263
|
+
query_normalized = cls._normalize(query)
|
|
264
|
+
return [p for p in cls._data if query_normalized in cls._normalize(p["plaza"])]
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def get_estadisticas(cls) -> dict:
|
|
268
|
+
"""
|
|
269
|
+
Obtiene estadísticas del catálogo.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Diccionario con estadísticas
|
|
273
|
+
"""
|
|
274
|
+
cls._load()
|
|
275
|
+
|
|
276
|
+
estados = {p["estado"] for p in cls._data}
|
|
277
|
+
codigos_unicos = len(cls._by_codigo)
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
"total_plazas": len(cls._data),
|
|
281
|
+
"codigos_unicos": codigos_unicos,
|
|
282
|
+
"estados_cubiertos": len(estados),
|
|
283
|
+
"plazas_duplicadas": len(cls.get_plazas_duplicadas()),
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
__all__ = ["CodigosPlazaCatalog", "CodigoPlaza"]
|