catalogmx 0.3.0__py3-none-any.whl → 0.4.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.
Files changed (53) hide show
  1. catalogmx/__init__.py +133 -19
  2. catalogmx/calculators/__init__.py +113 -0
  3. catalogmx/calculators/costo_trabajador.py +213 -0
  4. catalogmx/calculators/impuestos.py +920 -0
  5. catalogmx/calculators/imss.py +370 -0
  6. catalogmx/calculators/isr.py +290 -0
  7. catalogmx/calculators/resico.py +154 -0
  8. catalogmx/catalogs/banxico/__init__.py +29 -3
  9. catalogmx/catalogs/banxico/cetes_sqlite.py +279 -0
  10. catalogmx/catalogs/banxico/inflacion_sqlite.py +302 -0
  11. catalogmx/catalogs/banxico/salarios_minimos_sqlite.py +295 -0
  12. catalogmx/catalogs/banxico/tiie_sqlite.py +279 -0
  13. catalogmx/catalogs/banxico/tipo_cambio_usd_sqlite.py +255 -0
  14. catalogmx/catalogs/banxico/udis_sqlite.py +332 -0
  15. catalogmx/catalogs/cnbv/__init__.py +9 -0
  16. catalogmx/catalogs/cnbv/sectores.py +173 -0
  17. catalogmx/catalogs/conapo/__init__.py +15 -0
  18. catalogmx/catalogs/conapo/sistema_urbano_nacional.py +50 -0
  19. catalogmx/catalogs/conapo/zonas_metropolitanas.py +230 -0
  20. catalogmx/catalogs/ift/__init__.py +1 -1
  21. catalogmx/catalogs/ift/codigos_lada.py +517 -313
  22. catalogmx/catalogs/inegi/__init__.py +17 -0
  23. catalogmx/catalogs/inegi/scian.py +127 -0
  24. catalogmx/catalogs/mexico/__init__.py +2 -0
  25. catalogmx/catalogs/mexico/giros_mercantiles.py +119 -0
  26. catalogmx/catalogs/sat/carta_porte/material_peligroso.py +5 -1
  27. catalogmx/catalogs/sat/cfdi_4/clave_prod_serv.py +78 -0
  28. catalogmx/catalogs/sat/cfdi_4/tasa_o_cuota.py +2 -1
  29. catalogmx/catalogs/sepomex/__init__.py +2 -1
  30. catalogmx/catalogs/sepomex/codigos_postales.py +30 -2
  31. catalogmx/catalogs/sepomex/codigos_postales_completo.py +261 -0
  32. catalogmx/cli.py +12 -9
  33. catalogmx/data/__init__.py +10 -0
  34. catalogmx/data/mexico_dynamic.sqlite3 +0 -0
  35. catalogmx/data/updater.py +362 -0
  36. catalogmx/generators/__init__.py +20 -0
  37. catalogmx/generators/identity.py +582 -0
  38. catalogmx/helpers.py +177 -3
  39. catalogmx/utils/__init__.py +29 -0
  40. catalogmx/utils/clabe_utils.py +417 -0
  41. catalogmx/utils/text.py +7 -1
  42. catalogmx/validators/clabe.py +52 -2
  43. catalogmx/validators/nss.py +32 -27
  44. catalogmx/validators/rfc.py +185 -52
  45. catalogmx-0.4.0.dist-info/METADATA +905 -0
  46. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/RECORD +51 -25
  47. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/WHEEL +1 -1
  48. catalogmx/catalogs/banxico/udis.py +0 -279
  49. catalogmx-0.3.0.dist-info/METADATA +0 -644
  50. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/entry_points.txt +0 -0
  51. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/licenses/AUTHORS.rst +0 -0
  52. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/licenses/LICENSE +0 -0
  53. {catalogmx-0.3.0.dist-info → catalogmx-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,230 @@
1
+ """
2
+ Catálogo de Zonas Metropolitanas de México 2020
3
+
4
+ Fuente: SEDATU/INEGI/CONAPO - Metrópolis de México 2020
5
+ https://www.datos.gob.mx/dataset/metropolis_mexico_2020
6
+
7
+ Contiene 92 metrópolis conformadas por 421 municipios:
8
+ - 48 zonas metropolitanas (345 municipios)
9
+ - 22 metrópolis municipales
10
+ - 22 zonas conurbadas (54 municipios)
11
+ """
12
+
13
+ import csv
14
+ from pathlib import Path
15
+
16
+
17
+ class ZonasMetropolitanasCatalog:
18
+ """Catálogo de Zonas Metropolitanas de México 2020"""
19
+
20
+ _data: list[dict] | None = None
21
+ _by_clave: dict[str, dict] | None = None
22
+ _by_municipio: dict[str, list[dict]] | None = None
23
+ _metropolis: dict[str, dict] | None = None
24
+
25
+ @classmethod
26
+ def _load_data(cls) -> None:
27
+ """Carga los datos del catálogo"""
28
+ if cls._data is not None:
29
+ return
30
+
31
+ data_path = (
32
+ Path(__file__).resolve().parents[4]
33
+ / "shared-data"
34
+ / "conapo"
35
+ / "municipios_tipologia.csv"
36
+ )
37
+
38
+ cls._data = []
39
+ cls._by_clave = {}
40
+ cls._by_municipio = {}
41
+ cls._metropolis = {}
42
+
43
+ with open(data_path, encoding="utf-8") as f:
44
+ reader = csv.DictReader(f)
45
+ for row in reader:
46
+ # Normalizar claves INEGI
47
+ cve_ent = str(row["clave_entidad"]).zfill(2)
48
+ cve_mun = str(row["clave_municipio"]).zfill(3)
49
+ cve_compuesta = f"{cve_ent}{cve_mun}"
50
+
51
+ record = {
52
+ "clave_metropoli": row["clave_metropoli"],
53
+ "tipo": row["tipo"],
54
+ "nombre_metropoli": row["nombre"],
55
+ "cve_entidad": cve_ent,
56
+ "entidad": row["entidad"],
57
+ "cve_municipio": cve_mun,
58
+ "cve_inegi": cve_compuesta,
59
+ "municipio": row["municipio"],
60
+ "es_central": (
61
+ row.get("centrales_conurbacion_fisica") == "True"
62
+ or row.get("centrales_integracion_funcional") == "True"
63
+ or row.get("centrales_capital_200khab") == "True"
64
+ or row.get("centrales_0100khab") == "True"
65
+ or row.get("centrales_50khab") == "True"
66
+ ),
67
+ "es_exterior": (
68
+ row.get("exteriores_integracion_funcional") == "True"
69
+ or row.get("exteriores_continuidad_geografica") == "True"
70
+ ),
71
+ }
72
+
73
+ cls._data.append(record)
74
+ cls._by_clave[cve_compuesta] = record
75
+
76
+ # Index by municipio
77
+ if cve_compuesta not in cls._by_municipio:
78
+ cls._by_municipio[cve_compuesta] = []
79
+ cls._by_municipio[cve_compuesta].append(record)
80
+
81
+ # Build metropolis index
82
+ clave_m = row["clave_metropoli"]
83
+ if clave_m not in cls._metropolis:
84
+ cls._metropolis[clave_m] = {
85
+ "clave": clave_m,
86
+ "nombre": row["nombre"],
87
+ "tipo": row["tipo"],
88
+ "municipios": [],
89
+ }
90
+ cls._metropolis[clave_m]["municipios"].append(record)
91
+
92
+ @classmethod
93
+ def get_all(cls) -> list[dict]:
94
+ """Obtiene todos los registros de municipios metropolitanos"""
95
+ cls._load_data()
96
+ return cls._data.copy() if cls._data else []
97
+
98
+ @classmethod
99
+ def get_metropolis(cls) -> list[dict]:
100
+ """Obtiene lista de todas las metrópolis"""
101
+ cls._load_data()
102
+ if not cls._metropolis:
103
+ return []
104
+ return [
105
+ {
106
+ "clave": m["clave"],
107
+ "nombre": m["nombre"],
108
+ "tipo": m["tipo"],
109
+ "num_municipios": len(m["municipios"]),
110
+ }
111
+ for m in cls._metropolis.values()
112
+ ]
113
+
114
+ @classmethod
115
+ def get_por_tipo(cls, tipo: str) -> list[dict]:
116
+ """
117
+ Obtiene metrópolis por tipo.
118
+
119
+ Args:
120
+ tipo: 'Zona metropolitana', 'Metrópoli municipal', 'Zona conurbada'
121
+ """
122
+ cls._load_data()
123
+ if not cls._metropolis:
124
+ return []
125
+ return [
126
+ {
127
+ "clave": m["clave"],
128
+ "nombre": m["nombre"],
129
+ "tipo": m["tipo"],
130
+ "num_municipios": len(m["municipios"]),
131
+ }
132
+ for m in cls._metropolis.values()
133
+ if m["tipo"] == tipo
134
+ ]
135
+
136
+ @classmethod
137
+ def buscar_por_municipio(cls, cve_entidad: str, cve_municipio: str) -> dict | None:
138
+ """
139
+ Busca si un municipio pertenece a alguna metrópolis.
140
+
141
+ Args:
142
+ cve_entidad: Clave de entidad INEGI (01-32)
143
+ cve_municipio: Clave de municipio INEGI (001-570)
144
+
145
+ Returns:
146
+ Dict con información de la metrópolis o None
147
+ """
148
+ cls._load_data()
149
+ cve = f"{cve_entidad.zfill(2)}{cve_municipio.zfill(3)}"
150
+ records = cls._by_municipio.get(cve, []) if cls._by_municipio else []
151
+ return records[0] if records else None
152
+
153
+ @classmethod
154
+ def get_municipios_de_metropoli(cls, clave_metropoli: str) -> list[dict]:
155
+ """
156
+ Obtiene todos los municipios de una metrópolis.
157
+
158
+ Args:
159
+ clave_metropoli: Clave de la metrópolis (ej: "01.1.01")
160
+
161
+ Returns:
162
+ Lista de municipios con sus datos INEGI
163
+ """
164
+ cls._load_data()
165
+ if not cls._metropolis:
166
+ return []
167
+ m = cls._metropolis.get(clave_metropoli)
168
+ return m["municipios"].copy() if m else []
169
+
170
+ @classmethod
171
+ def buscar_por_nombre(cls, nombre: str) -> list[dict]:
172
+ """
173
+ Busca metrópolis por nombre (búsqueda parcial).
174
+
175
+ Args:
176
+ nombre: Nombre o parte del nombre de la metrópolis
177
+
178
+ Returns:
179
+ Lista de metrópolis que coinciden
180
+ """
181
+ cls._load_data()
182
+ if not cls._metropolis:
183
+ return []
184
+ nombre_upper = nombre.upper()
185
+ return [
186
+ {
187
+ "clave": m["clave"],
188
+ "nombre": m["nombre"],
189
+ "tipo": m["tipo"],
190
+ "num_municipios": len(m["municipios"]),
191
+ }
192
+ for m in cls._metropolis.values()
193
+ if nombre_upper in m["nombre"].upper()
194
+ ]
195
+
196
+ @classmethod
197
+ def es_municipio_metropolitano(cls, cve_entidad: str, cve_municipio: str) -> bool:
198
+ """
199
+ Verifica si un municipio pertenece a alguna metrópolis.
200
+
201
+ Args:
202
+ cve_entidad: Clave de entidad INEGI
203
+ cve_municipio: Clave de municipio INEGI
204
+
205
+ Returns:
206
+ True si el municipio es metropolitano
207
+ """
208
+ return cls.buscar_por_municipio(cve_entidad, cve_municipio) is not None
209
+
210
+ @classmethod
211
+ def get_estadisticas(cls) -> dict:
212
+ """Obtiene estadísticas del catálogo"""
213
+ cls._load_data()
214
+
215
+ if not cls._data or not cls._metropolis:
216
+ return {}
217
+
218
+ tipos = {}
219
+ for m in cls._metropolis.values():
220
+ t = m["tipo"]
221
+ if t not in tipos:
222
+ tipos[t] = {"metropolis": 0, "municipios": 0}
223
+ tipos[t]["metropolis"] += 1
224
+ tipos[t]["municipios"] += len(m["municipios"])
225
+
226
+ return {
227
+ "total_metropolis": len(cls._metropolis),
228
+ "total_municipios": len(cls._data),
229
+ "por_tipo": tipos,
230
+ }
@@ -2,7 +2,7 @@
2
2
  catalogmx.catalogs.ift - Catálogos del IFT
3
3
 
4
4
  Catálogos del Instituto Federal de Telecomunicaciones:
5
- - CodigosLADACatalog: Plan de numeración telefónica (códigos LADA)
5
+ - CodigosLADACatalog: Códigos LADA con mapeo geográfico INEGI
6
6
  - OperadoresMovilesCatalog: Operadores de telefonía móvil
7
7
  """
8
8