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,426 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Catálogo de códigos LADA (plan de numeración telefónica) en México (IFT)
|
|
3
|
+
|
|
4
|
+
Este módulo proporciona acceso al catálogo completo de códigos LADA
|
|
5
|
+
del Instituto Federal de Telecomunicaciones (IFT).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TypedDict
|
|
11
|
+
|
|
12
|
+
from catalogmx.utils.text import normalize_text
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CodigoLADA(TypedDict):
|
|
16
|
+
"""Estructura de un código LADA"""
|
|
17
|
+
|
|
18
|
+
lada: str
|
|
19
|
+
ciudad: str
|
|
20
|
+
estado: str
|
|
21
|
+
tipo: str # metropolitana | fronteriza | turistica | normal
|
|
22
|
+
region: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ValidacionNumero(TypedDict):
|
|
26
|
+
"""Resultado de validación de número telefónico"""
|
|
27
|
+
|
|
28
|
+
valid: bool
|
|
29
|
+
lada: str | None
|
|
30
|
+
numero_local: str | None
|
|
31
|
+
ciudad: str | None
|
|
32
|
+
estado: str | None
|
|
33
|
+
error: str | None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class InfoNumero(TypedDict):
|
|
37
|
+
"""Información detallada de un número telefónico"""
|
|
38
|
+
|
|
39
|
+
lada: str
|
|
40
|
+
ciudad: str
|
|
41
|
+
estado: str
|
|
42
|
+
tipo: str
|
|
43
|
+
region: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CodigosLADACatalog:
|
|
47
|
+
"""
|
|
48
|
+
Catálogo de códigos LADA de México.
|
|
49
|
+
|
|
50
|
+
Proporciona métodos para búsqueda, validación y formato de números telefónicos.
|
|
51
|
+
|
|
52
|
+
Características:
|
|
53
|
+
- 231+ códigos LADA (expandible a 397 según plan IFT)
|
|
54
|
+
- Cobertura nacional (32 estados)
|
|
55
|
+
- Clasificación por tipo (metropolitana, fronteriza, turística, normal)
|
|
56
|
+
- Validación de números telefónicos de 10 dígitos
|
|
57
|
+
- Formateo automático de números
|
|
58
|
+
|
|
59
|
+
Ejemplo:
|
|
60
|
+
>>> from catalogmx.catalogs.ift import CodigosLADACatalog
|
|
61
|
+
>>>
|
|
62
|
+
>>> # Buscar por LADA
|
|
63
|
+
>>> codigo = CodigosLADACatalog.buscar_por_lada("33")
|
|
64
|
+
>>> print(codigo['ciudad']) # "Guadalajara"
|
|
65
|
+
>>>
|
|
66
|
+
>>> # Validar número telefónico
|
|
67
|
+
>>> info = CodigosLADACatalog.validar_numero("3312345678")
|
|
68
|
+
>>> print(info['valid']) # True
|
|
69
|
+
>>> print(info['ciudad']) # "Guadalajara"
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
_data: list[CodigoLADA] | None = None
|
|
73
|
+
_by_lada: dict[str, CodigoLADA] | None = None
|
|
74
|
+
_by_estado: dict[str, list[CodigoLADA]] | None = None
|
|
75
|
+
_by_tipo: dict[str, list[CodigoLADA]] | None = None
|
|
76
|
+
_by_region: dict[str, list[CodigoLADA]] | None = None
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def _load_data(cls) -> None:
|
|
80
|
+
"""Carga lazy de datos desde JSON"""
|
|
81
|
+
if cls._data is not None:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# Path: catalogmx/packages/python/catalogmx/catalogs/ift/codigos_lada.py
|
|
85
|
+
# Target: catalogmx/packages/shared-data/ift/codigos_lada.json
|
|
86
|
+
data_path = (
|
|
87
|
+
Path(__file__).parent.parent.parent.parent.parent
|
|
88
|
+
/ "shared-data"
|
|
89
|
+
/ "ift"
|
|
90
|
+
/ "codigos_lada.json"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
with open(data_path, encoding="utf-8") as f:
|
|
94
|
+
json_data = json.load(f)
|
|
95
|
+
cls._data = json_data["codigos"]
|
|
96
|
+
|
|
97
|
+
# Crear índices para búsquedas rápidas
|
|
98
|
+
cls._by_lada = {item["lada"]: item for item in cls._data}
|
|
99
|
+
|
|
100
|
+
# Índice por estado
|
|
101
|
+
cls._by_estado = {}
|
|
102
|
+
for item in cls._data:
|
|
103
|
+
estado = item["estado"].lower()
|
|
104
|
+
if estado not in cls._by_estado:
|
|
105
|
+
cls._by_estado[estado] = []
|
|
106
|
+
cls._by_estado[estado].append(item)
|
|
107
|
+
|
|
108
|
+
# Índice por tipo
|
|
109
|
+
cls._by_tipo = {}
|
|
110
|
+
for item in cls._data:
|
|
111
|
+
tipo = item["tipo"]
|
|
112
|
+
if tipo not in cls._by_tipo:
|
|
113
|
+
cls._by_tipo[tipo] = []
|
|
114
|
+
cls._by_tipo[tipo].append(item)
|
|
115
|
+
|
|
116
|
+
# Índice por región
|
|
117
|
+
cls._by_region = {}
|
|
118
|
+
for item in cls._data:
|
|
119
|
+
region = item["region"].lower()
|
|
120
|
+
if region not in cls._by_region:
|
|
121
|
+
cls._by_region[region] = []
|
|
122
|
+
cls._by_region[region].append(item)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def get_all(cls) -> list[CodigoLADA]:
|
|
126
|
+
"""
|
|
127
|
+
Obtiene todos los códigos LADA.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Lista completa de códigos LADA
|
|
131
|
+
"""
|
|
132
|
+
cls._load_data()
|
|
133
|
+
return cls._data.copy() # type: ignore
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def buscar_por_lada(cls, lada: str) -> CodigoLADA | None:
|
|
137
|
+
"""
|
|
138
|
+
Busca un código LADA específico.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
lada: Código LADA a buscar (ej: "33", "55", "664")
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Información del código LADA o None si no existe
|
|
145
|
+
|
|
146
|
+
Ejemplo:
|
|
147
|
+
>>> codigo = CodigosLADACatalog.buscar_por_lada("33")
|
|
148
|
+
>>> print(codigo['ciudad']) # "Guadalajara"
|
|
149
|
+
>>> print(codigo['estado']) # "Jalisco"
|
|
150
|
+
"""
|
|
151
|
+
cls._load_data()
|
|
152
|
+
return cls._by_lada.get(lada) # type: ignore
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def buscar_por_ciudad(cls, ciudad: str) -> list[CodigoLADA]:
|
|
156
|
+
"""
|
|
157
|
+
Busca códigos LADA por nombre de ciudad (búsqueda parcial, insensible a acentos).
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
ciudad: Nombre o parte del nombre de la ciudad
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Lista de códigos LADA que coinciden
|
|
164
|
+
|
|
165
|
+
Ejemplo:
|
|
166
|
+
>>> # Búsqueda con o sin acentos funciona igual
|
|
167
|
+
>>> codigos = CodigosLADACatalog.buscar_por_ciudad("san jose")
|
|
168
|
+
>>> codigos = CodigosLADACatalog.buscar_por_ciudad("san josé") # mismo resultado
|
|
169
|
+
>>> for codigo in codigos:
|
|
170
|
+
... print(f"{codigo['lada']} - {codigo['ciudad']}")
|
|
171
|
+
"""
|
|
172
|
+
cls._load_data()
|
|
173
|
+
ciudad_normalized = normalize_text(ciudad)
|
|
174
|
+
return [
|
|
175
|
+
item
|
|
176
|
+
for item in cls._data # type: ignore
|
|
177
|
+
if ciudad_normalized in normalize_text(item["ciudad"])
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def get_por_estado(cls, estado: str) -> list[CodigoLADA]:
|
|
182
|
+
"""
|
|
183
|
+
Obtiene todos los códigos LADA de un estado.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
estado: Nombre del estado (ej: "Jalisco", "CDMX")
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Lista de códigos LADA del estado
|
|
190
|
+
|
|
191
|
+
Ejemplo:
|
|
192
|
+
>>> codigos = CodigosLADACatalog.get_por_estado("Jalisco")
|
|
193
|
+
>>> print(f"Jalisco tiene {len(codigos)} códigos LADA")
|
|
194
|
+
"""
|
|
195
|
+
cls._load_data()
|
|
196
|
+
estado_lower = estado.lower()
|
|
197
|
+
return cls._by_estado.get(estado_lower, []).copy() # type: ignore
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def get_por_tipo(cls, tipo: str) -> list[CodigoLADA]:
|
|
201
|
+
"""
|
|
202
|
+
Obtiene códigos LADA por tipo.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
tipo: Tipo de código ("metropolitana", "fronteriza", "turistica", "normal")
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Lista de códigos del tipo especificado
|
|
209
|
+
|
|
210
|
+
Ejemplo:
|
|
211
|
+
>>> metropolitanas = CodigosLADACatalog.get_por_tipo("metropolitana")
|
|
212
|
+
>>> for codigo in metropolitanas:
|
|
213
|
+
... print(f"{codigo['lada']} - {codigo['ciudad']}")
|
|
214
|
+
"""
|
|
215
|
+
cls._load_data()
|
|
216
|
+
return cls._by_tipo.get(tipo, []).copy() # type: ignore
|
|
217
|
+
|
|
218
|
+
@classmethod
|
|
219
|
+
def get_por_region(cls, region: str) -> list[CodigoLADA]:
|
|
220
|
+
"""
|
|
221
|
+
Obtiene códigos LADA por región geográfica.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
region: Región ("noroeste", "norte", "noreste", "occidente",
|
|
225
|
+
"centro", "golfo", "sur", "sureste")
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Lista de códigos de la región
|
|
229
|
+
|
|
230
|
+
Ejemplo:
|
|
231
|
+
>>> codigos_norte = CodigosLADACatalog.get_por_region("norte")
|
|
232
|
+
>>> print(f"Región norte: {len(codigos_norte)} códigos")
|
|
233
|
+
"""
|
|
234
|
+
cls._load_data()
|
|
235
|
+
region_lower = region.lower()
|
|
236
|
+
return cls._by_region.get(region_lower, []).copy() # type: ignore
|
|
237
|
+
|
|
238
|
+
@classmethod
|
|
239
|
+
def get_metropolitanas(cls) -> list[CodigoLADA]:
|
|
240
|
+
"""
|
|
241
|
+
Obtiene códigos LADA de zonas metropolitanas.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Lista de códigos metropolitanos
|
|
245
|
+
"""
|
|
246
|
+
return cls.get_por_tipo("metropolitana")
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
def get_fronterizas(cls) -> list[CodigoLADA]:
|
|
250
|
+
"""
|
|
251
|
+
Obtiene códigos LADA de ciudades fronterizas.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Lista de códigos fronterizos
|
|
255
|
+
"""
|
|
256
|
+
return cls.get_por_tipo("fronteriza")
|
|
257
|
+
|
|
258
|
+
@classmethod
|
|
259
|
+
def get_turisticas(cls) -> list[CodigoLADA]:
|
|
260
|
+
"""
|
|
261
|
+
Obtiene códigos LADA de destinos turísticos.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Lista de códigos turísticos
|
|
265
|
+
"""
|
|
266
|
+
return cls.get_por_tipo("turistica")
|
|
267
|
+
|
|
268
|
+
@classmethod
|
|
269
|
+
def validar_numero(cls, numero: str) -> ValidacionNumero:
|
|
270
|
+
"""
|
|
271
|
+
Valida y analiza un número telefónico mexicano.
|
|
272
|
+
|
|
273
|
+
Desde agosto 2019, México usa un plan de marcación cerrado de 10 dígitos.
|
|
274
|
+
Este método valida el formato y extrae información geográfica.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
numero: Número telefónico (puede contener espacios o guiones)
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Diccionario con validación e información del número
|
|
281
|
+
|
|
282
|
+
Ejemplo:
|
|
283
|
+
>>> info = CodigosLADACatalog.validar_numero("33 1234 5678")
|
|
284
|
+
>>> if info['valid']:
|
|
285
|
+
... print(f"LADA: {info['lada']}")
|
|
286
|
+
... print(f"Ciudad: {info['ciudad']}")
|
|
287
|
+
... print(f"Número local: {info['numero_local']}")
|
|
288
|
+
"""
|
|
289
|
+
cls._load_data()
|
|
290
|
+
|
|
291
|
+
# Limpiar número (eliminar espacios y guiones)
|
|
292
|
+
numero_limpio = numero.replace(" ", "").replace("-", "")
|
|
293
|
+
|
|
294
|
+
# Validar que sean 10 dígitos
|
|
295
|
+
if not numero_limpio.isdigit() or len(numero_limpio) != 10:
|
|
296
|
+
return {
|
|
297
|
+
"valid": False,
|
|
298
|
+
"lada": None,
|
|
299
|
+
"numero_local": None,
|
|
300
|
+
"ciudad": None,
|
|
301
|
+
"estado": None,
|
|
302
|
+
"error": "El número debe tener exactamente 10 dígitos",
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# Intentar extraer LADA (primeros 2 o 3 dígitos)
|
|
306
|
+
# Primero intentar con 3 dígitos
|
|
307
|
+
lada = numero_limpio[:3]
|
|
308
|
+
codigo = cls._by_lada.get(lada) # type: ignore
|
|
309
|
+
|
|
310
|
+
# Si no se encuentra, intentar con 2 dígitos
|
|
311
|
+
if not codigo:
|
|
312
|
+
lada = numero_limpio[:2]
|
|
313
|
+
codigo = cls._by_lada.get(lada) # type: ignore
|
|
314
|
+
|
|
315
|
+
if codigo:
|
|
316
|
+
numero_local = numero_limpio[len(lada) :]
|
|
317
|
+
return {
|
|
318
|
+
"valid": True,
|
|
319
|
+
"lada": codigo["lada"],
|
|
320
|
+
"numero_local": numero_local,
|
|
321
|
+
"ciudad": codigo["ciudad"],
|
|
322
|
+
"estado": codigo["estado"],
|
|
323
|
+
"error": None,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
"valid": False,
|
|
328
|
+
"lada": lada,
|
|
329
|
+
"numero_local": None,
|
|
330
|
+
"ciudad": None,
|
|
331
|
+
"estado": None,
|
|
332
|
+
"error": f"Código LADA {lada} no encontrado en el catálogo",
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@classmethod
|
|
336
|
+
def formatear_numero(cls, numero: str) -> str:
|
|
337
|
+
"""
|
|
338
|
+
Formatea un número telefónico al formato estándar.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
numero: Número telefónico sin formato
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Número formateado (LADA XXXX XXXX)
|
|
345
|
+
|
|
346
|
+
Ejemplo:
|
|
347
|
+
>>> formateado = CodigosLADACatalog.formatear_numero("3312345678")
|
|
348
|
+
>>> print(formateado) # "33 1234 5678"
|
|
349
|
+
"""
|
|
350
|
+
validacion = cls.validar_numero(numero)
|
|
351
|
+
|
|
352
|
+
if not validacion["valid"] or not validacion["lada"] or not validacion["numero_local"]:
|
|
353
|
+
return numero
|
|
354
|
+
|
|
355
|
+
lada = validacion["lada"]
|
|
356
|
+
local = validacion["numero_local"]
|
|
357
|
+
|
|
358
|
+
# Formato: LADA XXXX XXXX
|
|
359
|
+
if len(local) == 7:
|
|
360
|
+
return f"{lada} {local[:3]} {local[3:]}"
|
|
361
|
+
elif len(local) == 8:
|
|
362
|
+
return f"{lada} {local[:4]} {local[4:]}"
|
|
363
|
+
else:
|
|
364
|
+
return f"{lada} {local}"
|
|
365
|
+
|
|
366
|
+
@classmethod
|
|
367
|
+
def get_info_numero(cls, numero: str) -> InfoNumero | None:
|
|
368
|
+
"""
|
|
369
|
+
Obtiene información completa de un número telefónico.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
numero: Número telefónico
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Información completa del número o None si es inválido
|
|
376
|
+
|
|
377
|
+
Ejemplo:
|
|
378
|
+
>>> info = CodigosLADACatalog.get_info_numero("3312345678")
|
|
379
|
+
>>> if info:
|
|
380
|
+
... print(f"Ciudad: {info['ciudad']}")
|
|
381
|
+
... print(f"Estado: {info['estado']}")
|
|
382
|
+
... print(f"Tipo: {info['tipo']}")
|
|
383
|
+
... print(f"Región: {info['region']}")
|
|
384
|
+
"""
|
|
385
|
+
validacion = cls.validar_numero(numero)
|
|
386
|
+
|
|
387
|
+
if not validacion["valid"] or not validacion["lada"]:
|
|
388
|
+
return None
|
|
389
|
+
|
|
390
|
+
codigo = cls.buscar_por_lada(validacion["lada"])
|
|
391
|
+
|
|
392
|
+
if not codigo:
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
"lada": codigo["lada"],
|
|
397
|
+
"ciudad": codigo["ciudad"],
|
|
398
|
+
"estado": codigo["estado"],
|
|
399
|
+
"tipo": codigo["tipo"],
|
|
400
|
+
"region": codigo["region"],
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
@classmethod
|
|
404
|
+
def get_estadisticas(cls) -> dict[str, int | dict]:
|
|
405
|
+
"""
|
|
406
|
+
Obtiene estadísticas del catálogo.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Diccionario con estadísticas del catálogo
|
|
410
|
+
|
|
411
|
+
Ejemplo:
|
|
412
|
+
>>> stats = CodigosLADACatalog.get_estadisticas()
|
|
413
|
+
>>> print(f"Total códigos: {stats['total_codigos']}")
|
|
414
|
+
>>> print(f"Estados: {stats['estados_cubiertos']}")
|
|
415
|
+
"""
|
|
416
|
+
cls._load_data()
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
"total_codigos": len(cls._data), # type: ignore
|
|
420
|
+
"codigos_metropolitanos": len(cls.get_metropolitanas()),
|
|
421
|
+
"codigos_fronterizos": len(cls.get_fronterizas()),
|
|
422
|
+
"codigos_turisticos": len(cls.get_turisticas()),
|
|
423
|
+
"estados_cubiertos": len(cls._by_estado), # type: ignore
|
|
424
|
+
"regiones": list(cls._by_region.keys()), # type: ignore
|
|
425
|
+
"tipos": list(cls._by_tipo.keys()), # type: ignore
|
|
426
|
+
}
|