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
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
INEGI - Catálogo Completo de Municipios Mexicanos
|
|
3
|
+
|
|
4
|
+
Catálogo completo de todos los 2,469 municipios de México
|
|
5
|
+
(2,462 municipios + 7 alcaldías CDMX)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MunicipiosCompletoCatalog:
|
|
13
|
+
"""
|
|
14
|
+
Catálogo completo de municipios mexicanos.
|
|
15
|
+
|
|
16
|
+
Incluye todos los 2,469 municipios (2,462 municipios + 7 alcaldías CDMX)
|
|
17
|
+
con información demográfica completa.
|
|
18
|
+
|
|
19
|
+
WARNING: Este catálogo carga todos los municipios en memoria (~940KB).
|
|
20
|
+
Para consultas simples, considere usar MunicipiosCatalog en su lugar.
|
|
21
|
+
|
|
22
|
+
Características:
|
|
23
|
+
- 2,469 municipios totales
|
|
24
|
+
- Población total, masculina, femenina
|
|
25
|
+
- Viviendas habitadas
|
|
26
|
+
- Nombre de cabecera municipal
|
|
27
|
+
- Códigos INEGI completos
|
|
28
|
+
|
|
29
|
+
Ejemplo:
|
|
30
|
+
>>> from catalogmx.catalogs.inegi import MunicipiosCompletoCatalog
|
|
31
|
+
>>>
|
|
32
|
+
>>> # Obtener todos los municipios
|
|
33
|
+
>>> municipios = MunicipiosCompletoCatalog.get_all()
|
|
34
|
+
>>> print(f"Total municipios: {len(municipios)}")
|
|
35
|
+
>>>
|
|
36
|
+
>>> # Buscar municipio por código
|
|
37
|
+
>>> guadalajara = MunicipiosCompletoCatalog.get_municipio("14039")
|
|
38
|
+
>>> print(f"{guadalajara['nom_municipio']}: {guadalajara['poblacion_total']:,} habitantes")
|
|
39
|
+
>>>
|
|
40
|
+
>>> # Obtener municipios por estado
|
|
41
|
+
>>> jalisco = MunicipiosCompletoCatalog.get_by_entidad("14")
|
|
42
|
+
>>> print(f"Jalisco tiene {len(jalisco)} municipios")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
_data: list[dict] | None = None
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def _load_data(cls) -> None:
|
|
49
|
+
"""Carga lazy de datos desde JSON"""
|
|
50
|
+
if cls._data is not None:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# Path: catalogmx/packages/python/catalogmx/catalogs/inegi/municipios_completo.py
|
|
54
|
+
# Target: catalogmx/packages/shared-data/inegi/municipios_completo.json
|
|
55
|
+
data_path = (
|
|
56
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
57
|
+
/ "shared-data"
|
|
58
|
+
/ "inegi"
|
|
59
|
+
/ "municipios_completo.json"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
with open(data_path, encoding="utf-8") as f:
|
|
63
|
+
cls._data = json.load(f)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def get_all(cls) -> list[dict]:
|
|
67
|
+
"""
|
|
68
|
+
Obtiene todos los municipios (2,469 total).
|
|
69
|
+
|
|
70
|
+
WARNING: Retorna todos los municipios en memoria.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Lista completa de municipios
|
|
74
|
+
|
|
75
|
+
Ejemplo:
|
|
76
|
+
>>> municipios = MunicipiosCompletoCatalog.get_all()
|
|
77
|
+
>>> print(f"Total: {len(municipios)}") # 2469
|
|
78
|
+
"""
|
|
79
|
+
cls._load_data()
|
|
80
|
+
return cls._data.copy() # type: ignore
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def get_municipio(cls, cve_completa: str) -> dict | None:
|
|
84
|
+
"""
|
|
85
|
+
Obtiene municipio por código completo (cve_completa).
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
cve_completa: Código completo de 5 dígitos (ej: "14039" para Guadalajara)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Información del municipio o None si no existe
|
|
92
|
+
|
|
93
|
+
Ejemplo:
|
|
94
|
+
>>> mun = MunicipiosCompletoCatalog.get_municipio("14039")
|
|
95
|
+
>>> print(mun['nom_municipio']) # "Guadalajara"
|
|
96
|
+
"""
|
|
97
|
+
cls._load_data()
|
|
98
|
+
for mun in cls._data: # type: ignore
|
|
99
|
+
if mun["cve_completa"] == cve_completa:
|
|
100
|
+
return mun
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def get_by_entidad(cls, cve_entidad: str) -> list[dict]:
|
|
105
|
+
"""
|
|
106
|
+
Obtiene municipios por estado (cve_entidad).
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
cve_entidad: Código de entidad de 2 dígitos (ej: "14" para Jalisco)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Lista de municipios del estado
|
|
113
|
+
|
|
114
|
+
Ejemplo:
|
|
115
|
+
>>> jalisco = MunicipiosCompletoCatalog.get_by_entidad("14")
|
|
116
|
+
>>> print(f"Jalisco: {len(jalisco)} municipios")
|
|
117
|
+
"""
|
|
118
|
+
cls._load_data()
|
|
119
|
+
return [mun for mun in cls._data if mun["cve_entidad"] == cve_entidad] # type: ignore
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def search_by_name(cls, name: str) -> list[dict]:
|
|
123
|
+
"""
|
|
124
|
+
Busca municipios por nombre (case-insensitive).
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
name: Nombre o parte del nombre a buscar
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Lista de municipios que coinciden
|
|
131
|
+
|
|
132
|
+
Ejemplo:
|
|
133
|
+
>>> san = MunicipiosCompletoCatalog.search_by_name("San")
|
|
134
|
+
>>> for mun in san[:5]:
|
|
135
|
+
... print(f"{mun['nom_municipio']}, {mun['nom_entidad']}")
|
|
136
|
+
"""
|
|
137
|
+
cls._load_data()
|
|
138
|
+
search_term = name.upper()
|
|
139
|
+
return [
|
|
140
|
+
mun for mun in cls._data if search_term in mun["nom_municipio"].upper() # type: ignore
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def get_by_state_name(cls, state_name: str) -> list[dict]:
|
|
145
|
+
"""
|
|
146
|
+
Obtiene municipios por nombre de estado.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
state_name: Nombre del estado (ej: "Jalisco", "CDMX")
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Lista de municipios del estado
|
|
153
|
+
|
|
154
|
+
Ejemplo:
|
|
155
|
+
>>> jalisco = MunicipiosCompletoCatalog.get_by_state_name("Jalisco")
|
|
156
|
+
>>> for mun in jalisco[:5]:
|
|
157
|
+
... print(mun['nom_municipio'])
|
|
158
|
+
"""
|
|
159
|
+
cls._load_data()
|
|
160
|
+
search_term = state_name.upper()
|
|
161
|
+
return [
|
|
162
|
+
mun for mun in cls._data if mun["nom_entidad"].upper() == search_term # type: ignore
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def get_count_by_entidad(cls, cve_entidad: str) -> int:
|
|
167
|
+
"""
|
|
168
|
+
Obtiene el número de municipios en un estado.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
cve_entidad: Código de entidad de 2 dígitos
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Número de municipios en el estado
|
|
175
|
+
|
|
176
|
+
Ejemplo:
|
|
177
|
+
>>> count = MunicipiosCompletoCatalog.get_count_by_entidad("14")
|
|
178
|
+
>>> print(f"Jalisco tiene {count} municipios")
|
|
179
|
+
"""
|
|
180
|
+
return len(cls.get_by_entidad(cve_entidad))
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def is_valid(cls, cve_completa: str) -> bool:
|
|
184
|
+
"""
|
|
185
|
+
Valida código de municipio.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
cve_completa: Código completo de 5 dígitos
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
True si el código existe, False en caso contrario
|
|
192
|
+
|
|
193
|
+
Ejemplo:
|
|
194
|
+
>>> MunicipiosCompletoCatalog.is_valid("14039") # True
|
|
195
|
+
>>> MunicipiosCompletoCatalog.is_valid("99999") # False
|
|
196
|
+
"""
|
|
197
|
+
return cls.get_municipio(cve_completa) is not None
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def get_total_count(cls) -> int:
|
|
201
|
+
"""
|
|
202
|
+
Obtiene el total de municipios.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Número total de municipios (2,469)
|
|
206
|
+
|
|
207
|
+
Ejemplo:
|
|
208
|
+
>>> total = MunicipiosCompletoCatalog.get_total_count()
|
|
209
|
+
>>> print(f"Total municipios: {total}") # 2469
|
|
210
|
+
"""
|
|
211
|
+
cls._load_data()
|
|
212
|
+
return len(cls._data) # type: ignore
|
|
213
|
+
|
|
214
|
+
@classmethod
|
|
215
|
+
def get_estadisticas(cls) -> dict[str, int]:
|
|
216
|
+
"""
|
|
217
|
+
Obtiene estadísticas del catálogo.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Diccionario con estadísticas de municipios
|
|
221
|
+
|
|
222
|
+
Ejemplo:
|
|
223
|
+
>>> stats = MunicipiosCompletoCatalog.get_estadisticas()
|
|
224
|
+
>>> print(f"Total municipios: {stats['total_municipios']}")
|
|
225
|
+
>>> print(f"Total estados: {stats['total_estados']}")
|
|
226
|
+
"""
|
|
227
|
+
cls._load_data()
|
|
228
|
+
|
|
229
|
+
estados = {mun["cve_entidad"] for mun in cls._data} # type: ignore
|
|
230
|
+
poblacion_total = sum(mun["poblacion_total"] for mun in cls._data) # type: ignore
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
"total_municipios": len(cls._data), # type: ignore
|
|
234
|
+
"total_estados": len(estados),
|
|
235
|
+
"poblacion_total": poblacion_total,
|
|
236
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mexican States Catalog from INEGI
|
|
3
|
+
|
|
4
|
+
This module provides access to the official catalog of Mexican states
|
|
5
|
+
(entidades federativas) with their CURP codes, INEGI codes, and abbreviations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from catalogmx.utils.text import normalize_text
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StateCatalog:
|
|
15
|
+
"""
|
|
16
|
+
Catalog of Mexican states
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_data: list[dict] | None = None
|
|
20
|
+
_state_by_code: dict[str, dict] | None = None
|
|
21
|
+
_state_by_name: dict[str, dict] | None = None
|
|
22
|
+
_state_by_name_normalized: dict[str, dict] | None = None
|
|
23
|
+
_state_by_inegi: dict[str, dict] | None = None
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _load_data(cls) -> None:
|
|
27
|
+
"""Load state data from JSON file"""
|
|
28
|
+
if cls._data is None:
|
|
29
|
+
# Get path to shared data
|
|
30
|
+
current_file = Path(__file__)
|
|
31
|
+
shared_data_path = (
|
|
32
|
+
current_file.parent.parent.parent.parent.parent
|
|
33
|
+
/ "shared-data"
|
|
34
|
+
/ "inegi"
|
|
35
|
+
/ "states.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._state_by_code = {state["code"]: state for state in cls._data}
|
|
43
|
+
cls._state_by_name = {state["name"].upper(): state for state in cls._data}
|
|
44
|
+
cls._state_by_name_normalized = {
|
|
45
|
+
normalize_text(state["name"]): state for state in cls._data
|
|
46
|
+
}
|
|
47
|
+
cls._state_by_inegi = {state["clave_inegi"]: state for state in cls._data}
|
|
48
|
+
|
|
49
|
+
# Add aliases to name lookup
|
|
50
|
+
for state in cls._data:
|
|
51
|
+
if "aliases" in state:
|
|
52
|
+
for alias in state["aliases"]:
|
|
53
|
+
cls._state_by_name[alias.upper()] = state
|
|
54
|
+
cls._state_by_name_normalized[normalize_text(alias)] = state
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def get_all_states(cls) -> list[dict]:
|
|
58
|
+
"""
|
|
59
|
+
Get all states in the catalog
|
|
60
|
+
|
|
61
|
+
:return: List of state dictionaries
|
|
62
|
+
"""
|
|
63
|
+
cls._load_data()
|
|
64
|
+
return cls._data.copy()
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def get_state_by_code(cls, code: str) -> dict | None:
|
|
68
|
+
"""
|
|
69
|
+
Get state information by CURP code
|
|
70
|
+
|
|
71
|
+
:param code: 2-letter CURP state code (e.g., 'AS' for Aguascalientes)
|
|
72
|
+
:return: State dictionary or None if not found
|
|
73
|
+
"""
|
|
74
|
+
cls._load_data()
|
|
75
|
+
return cls._state_by_code.get(code.upper())
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def get_state_by_name(cls, name: str) -> dict | None:
|
|
79
|
+
"""
|
|
80
|
+
Get state information by name (case and accent insensitive)
|
|
81
|
+
|
|
82
|
+
:param name: State name (case and accent insensitive)
|
|
83
|
+
:return: State dictionary or None if not found
|
|
84
|
+
"""
|
|
85
|
+
cls._load_data()
|
|
86
|
+
return cls._state_by_name_normalized.get(normalize_text(name))
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def get_state_by_inegi_code(cls, code: str) -> dict | None:
|
|
90
|
+
"""
|
|
91
|
+
Get state information by INEGI code
|
|
92
|
+
|
|
93
|
+
:param code: 2-digit INEGI code (e.g., '01' for Aguascalientes)
|
|
94
|
+
:return: State dictionary or None if not found
|
|
95
|
+
"""
|
|
96
|
+
cls._load_data()
|
|
97
|
+
code = str(code).zfill(2)
|
|
98
|
+
return cls._state_by_inegi.get(code)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def validate_state_code(cls, code: str) -> bool:
|
|
102
|
+
"""
|
|
103
|
+
Validate if a state CURP code exists
|
|
104
|
+
|
|
105
|
+
:param code: 2-letter CURP state code
|
|
106
|
+
:return: True if exists, False otherwise
|
|
107
|
+
"""
|
|
108
|
+
return cls.get_state_by_code(code) is not None
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def get_state_codes(cls) -> dict[str, str]:
|
|
112
|
+
"""
|
|
113
|
+
Get dictionary of state names to CURP codes
|
|
114
|
+
|
|
115
|
+
:return: Dictionary mapping state names to codes
|
|
116
|
+
"""
|
|
117
|
+
cls._load_data()
|
|
118
|
+
return {state["name"]: state["code"] for state in cls._data}
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def get_inegi_codes(cls) -> dict[str, str]:
|
|
122
|
+
"""
|
|
123
|
+
Get dictionary of state names to INEGI codes
|
|
124
|
+
|
|
125
|
+
:return: Dictionary mapping state names to INEGI codes
|
|
126
|
+
"""
|
|
127
|
+
cls._load_data()
|
|
128
|
+
return {state["name"]: state["clave_inegi"] for state in cls._data}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# Convenience functions
|
|
132
|
+
def get_states_dict() -> dict[str, dict]:
|
|
133
|
+
"""Get dictionary of all states indexed by CURP code"""
|
|
134
|
+
StateCatalog._load_data()
|
|
135
|
+
return StateCatalog._state_by_code.copy()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_state_names() -> list[str]:
|
|
139
|
+
"""Get list of all state names"""
|
|
140
|
+
return [state["name"] for state in StateCatalog.get_all_states()]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Export commonly used functions
|
|
144
|
+
__all__ = [
|
|
145
|
+
"StateCatalog",
|
|
146
|
+
"get_states_dict",
|
|
147
|
+
"get_state_names",
|
|
148
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mexican National Catalogs
|
|
3
|
+
|
|
4
|
+
This module provides access to various Mexican national catalogs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .hoy_no_circula import HoyNoCirculaCatalog
|
|
8
|
+
from .placas_formatos import PlacasFormatosCatalog
|
|
9
|
+
from .salarios_minimos import SalariosMinimos
|
|
10
|
+
from .uma import UMACatalog
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"PlacasFormatosCatalog",
|
|
14
|
+
"SalariosMinimos",
|
|
15
|
+
"UMACatalog",
|
|
16
|
+
"HoyNoCirculaCatalog",
|
|
17
|
+
]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hoy No Circula CDMX Catalog
|
|
3
|
+
|
|
4
|
+
This module provides access to the Hoy No Circula traffic restriction program
|
|
5
|
+
for Mexico City (CDMX) and Estado de México.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HoyNoCirculaCatalog:
|
|
13
|
+
"""
|
|
14
|
+
Catalog for Hoy No Circula traffic restriction program
|
|
15
|
+
|
|
16
|
+
The Hoy No Circula program restricts vehicle circulation in Mexico City
|
|
17
|
+
and Estado de México based on the last digit of the license plate number
|
|
18
|
+
and the vehicle's verification hologram.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
_data: dict | None = None
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def _load_data(cls) -> None:
|
|
25
|
+
"""Load Hoy No Circula data from JSON file"""
|
|
26
|
+
if cls._data is None:
|
|
27
|
+
# Path: catalogmx/packages/python/catalogmx/catalogs/mexico/hoy_no_circula.py
|
|
28
|
+
# Target: catalogmx/packages/shared-data/mexico/hoy_no_circula_cdmx.json
|
|
29
|
+
current_file = Path(__file__)
|
|
30
|
+
shared_data_path = (
|
|
31
|
+
current_file.parent.parent.parent.parent.parent
|
|
32
|
+
/ "shared-data"
|
|
33
|
+
/ "mexico"
|
|
34
|
+
/ "hoy_no_circula_cdmx.json"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
with open(shared_data_path, encoding="utf-8") as f:
|
|
38
|
+
cls._data = json.load(f)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def get_data(cls) -> dict:
|
|
42
|
+
"""
|
|
43
|
+
Get all Hoy No Circula data
|
|
44
|
+
|
|
45
|
+
:return: Complete Hoy No Circula data dictionary
|
|
46
|
+
"""
|
|
47
|
+
cls._load_data()
|
|
48
|
+
return cls._data.copy()
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def get_restricciones(cls) -> list[dict]:
|
|
52
|
+
"""
|
|
53
|
+
Get all restrictions by day of week
|
|
54
|
+
|
|
55
|
+
:return: List of restriction dictionaries
|
|
56
|
+
"""
|
|
57
|
+
cls._load_data()
|
|
58
|
+
return cls._data.get("restricciones_por_dia", []).copy()
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def get_restriccion_por_dia(cls, dia: str) -> dict | None:
|
|
62
|
+
"""
|
|
63
|
+
Get restriction for a specific day of week
|
|
64
|
+
|
|
65
|
+
:param dia: Day of week (e.g., 'lunes', 'martes')
|
|
66
|
+
:return: Restriction dictionary or None if not found
|
|
67
|
+
"""
|
|
68
|
+
cls._load_data()
|
|
69
|
+
restricciones = cls._data.get("restricciones_por_dia", [])
|
|
70
|
+
|
|
71
|
+
for restriccion in restricciones:
|
|
72
|
+
if restriccion.get("dia", "").lower() == dia.lower():
|
|
73
|
+
return restriccion.copy()
|
|
74
|
+
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def get_exenciones(cls) -> list[dict]:
|
|
79
|
+
"""
|
|
80
|
+
Get all exemptions by hologram type
|
|
81
|
+
|
|
82
|
+
:return: List of exemption dictionaries
|
|
83
|
+
"""
|
|
84
|
+
cls._load_data()
|
|
85
|
+
return cls._data.get("exenciones_por_holograma", []).copy()
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def get_exencion_por_holograma(cls, holograma: str) -> dict | None:
|
|
89
|
+
"""
|
|
90
|
+
Get exemption information for a specific hologram
|
|
91
|
+
|
|
92
|
+
:param holograma: Hologram type (e.g., '00', '0', '1', '2')
|
|
93
|
+
:return: Exemption dictionary or None if not found
|
|
94
|
+
"""
|
|
95
|
+
cls._load_data()
|
|
96
|
+
exenciones = cls._data.get("exenciones_por_holograma", [])
|
|
97
|
+
|
|
98
|
+
for exencion in exenciones:
|
|
99
|
+
if exencion.get("holograma") == holograma:
|
|
100
|
+
return exencion.copy()
|
|
101
|
+
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def puede_circular(cls, terminacion: str, dia: str, holograma: str = "2") -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Check if a vehicle can circulate on a given day
|
|
108
|
+
|
|
109
|
+
:param terminacion: Last digit of license plate (0-9)
|
|
110
|
+
:param dia: Day of week ('lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado')
|
|
111
|
+
:param holograma: Verification hologram ('00', '0', '1', '2')
|
|
112
|
+
:return: True if can circulate, False otherwise
|
|
113
|
+
"""
|
|
114
|
+
cls._load_data()
|
|
115
|
+
|
|
116
|
+
# Check exemption by hologram first
|
|
117
|
+
exencion = cls.get_exencion_por_holograma(holograma)
|
|
118
|
+
if exencion:
|
|
119
|
+
# Hologram 00 and 0 can circulate all days (except Saturdays for 0)
|
|
120
|
+
if holograma == "00":
|
|
121
|
+
return True
|
|
122
|
+
elif holograma == "0":
|
|
123
|
+
return dia.lower() != "sábado"
|
|
124
|
+
|
|
125
|
+
# Check restriction for the day
|
|
126
|
+
restriccion = cls.get_restriccion_por_dia(dia)
|
|
127
|
+
if not restriccion:
|
|
128
|
+
# No restriction for this day (e.g., Sunday)
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
# Check if the termination is restricted
|
|
132
|
+
terminaciones_restringidas = restriccion.get("terminacion_placa", [])
|
|
133
|
+
return str(terminacion) not in [str(t) for t in terminaciones_restringidas]
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def get_dia_restriccion(cls, terminacion: str) -> str | None:
|
|
137
|
+
"""
|
|
138
|
+
Get the day of week when a vehicle is restricted (for hologram 2)
|
|
139
|
+
|
|
140
|
+
:param terminacion: Last digit of license plate (0-9)
|
|
141
|
+
:return: Day of week when restricted or None if not restricted
|
|
142
|
+
"""
|
|
143
|
+
cls._load_data()
|
|
144
|
+
|
|
145
|
+
restricciones = cls._data.get("restricciones_por_dia", [])
|
|
146
|
+
for restriccion in restricciones:
|
|
147
|
+
terminaciones = restriccion.get("terminacion_placa", [])
|
|
148
|
+
if str(terminacion) in [str(t) for t in terminaciones]:
|
|
149
|
+
return restriccion.get("dia")
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def get_engomado(cls, terminacion: str) -> str | None:
|
|
155
|
+
"""
|
|
156
|
+
Get the engomado (sticker color) for a license plate termination
|
|
157
|
+
|
|
158
|
+
:param terminacion: Last digit of license plate (0-9)
|
|
159
|
+
:return: Engomado color or None if not found
|
|
160
|
+
"""
|
|
161
|
+
cls._load_data()
|
|
162
|
+
|
|
163
|
+
restricciones = cls._data.get("restricciones_por_dia", [])
|
|
164
|
+
for restriccion in restricciones:
|
|
165
|
+
terminaciones = restriccion.get("terminacion_placa", [])
|
|
166
|
+
if str(terminacion) in [str(t) for t in terminaciones]:
|
|
167
|
+
engomados = restriccion.get("engomado", [])
|
|
168
|
+
return engomados[0] if engomados else None
|
|
169
|
+
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
@classmethod
|
|
173
|
+
def get_contingencias(cls) -> dict:
|
|
174
|
+
"""
|
|
175
|
+
Get contingency program information
|
|
176
|
+
|
|
177
|
+
:return: Dictionary with environmental contingency rules
|
|
178
|
+
"""
|
|
179
|
+
cls._load_data()
|
|
180
|
+
return cls._data.get("contingencias_ambientales", {}).copy()
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def get_sabatinos(cls) -> dict:
|
|
184
|
+
"""
|
|
185
|
+
Get Saturday restriction information
|
|
186
|
+
|
|
187
|
+
:return: Dictionary with Saturday restriction rules
|
|
188
|
+
"""
|
|
189
|
+
cls._load_data()
|
|
190
|
+
return cls._data.get("sabatinos", {}).copy()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Convenience functions
|
|
194
|
+
def puede_circular(terminacion: str, dia: str, holograma: str = "2") -> bool:
|
|
195
|
+
"""Check if a vehicle can circulate on a given day"""
|
|
196
|
+
return HoyNoCirculaCatalog.puede_circular(terminacion, dia, holograma)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def get_dia_restriccion(terminacion: str) -> str | None:
|
|
200
|
+
"""Get the day when a vehicle is restricted"""
|
|
201
|
+
return HoyNoCirculaCatalog.get_dia_restriccion(terminacion)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_engomado(terminacion: str) -> str | None:
|
|
205
|
+
"""Get the engomado color for a license plate termination"""
|
|
206
|
+
return HoyNoCirculaCatalog.get_engomado(terminacion)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Export commonly used functions and classes
|
|
210
|
+
__all__ = [
|
|
211
|
+
"HoyNoCirculaCatalog",
|
|
212
|
+
"puede_circular",
|
|
213
|
+
"get_dia_restriccion",
|
|
214
|
+
"get_engomado",
|
|
215
|
+
]
|