Actualización de Fase 1
Se ha cambiado la base de datos a Postgres SQL, además se han realizado algunos cambios de lógica.
This commit is contained in:
parent
6ef0203a7d
commit
a24ba9b4d1
58
Obtener-Datos-1/README.md
Normal file
58
Obtener-Datos-1/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Fase 1 — Ingesta Automática de Datos (Adicciones ↔ Violencia)
|
||||
|
||||
Esta fase inicial prepara la base de datos PostgreSQL para el análisis posterior.
|
||||
El script **detecta automáticamente delimitadores, codificaciones y nombres de columnas**,
|
||||
creando tablas limpias e importando los datasets CSV del directorio `datasets/`.
|
||||
|
||||
---
|
||||
|
||||
## Ejecución
|
||||
|
||||
1. Levanta el contenedor de PostgreSQL:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
2. Ejecuta el importador de datos:
|
||||
|
||||
```bash
|
||||
python3 importador.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Descripción Técnica
|
||||
|
||||
- Detecta **codificación** (`UTF-8`, `Latin1`, `Windows-1252`, etc.) con `chardet`.
|
||||
- Identifica el **delimitador CSV** (`;`, `,`, `|`, `\t`).
|
||||
- Limpia nombres de columnas y crea las tablas en **PostgreSQL 15**.
|
||||
- Inserta automáticamente los registros de cada dataset.
|
||||
- Genera el archivo de metadatos `columnas_info.json` con las columnas detectadas.
|
||||
|
||||
---
|
||||
|
||||
## Requisitos
|
||||
|
||||
Archivo `requirements.txt`:
|
||||
|
||||
```txt
|
||||
pandas
|
||||
psycopg2-binary
|
||||
chardet
|
||||
```
|
||||
|
||||
Instalación rápida:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Salida
|
||||
|
||||
- Tablas generadas en la base de datos `adicciones` (puerto 5433).
|
||||
- Archivo `columnas_info.json` con las estructuras detectadas.
|
||||
|
||||
*Esta fase deja lista la base de datos para las siguientes fases de procesamiento y análisis estadístico.*
|
||||
@ -26,7 +26,11 @@
|
||||
"prov"
|
||||
],
|
||||
"condenas_sexo_localidad": [
|
||||
"lugar_de_nacimiento_sexo_comunidades_y_ciudades_au"
|
||||
"lugar_de_nacimiento",
|
||||
"sexo",
|
||||
"comunidades_y_ciudades_aut_nomas",
|
||||
"periodo",
|
||||
"total"
|
||||
],
|
||||
"registro_prohibidos_juego": [
|
||||
"a_o",
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: adicciones_db
|
||||
postgres:
|
||||
image: postgres:15
|
||||
container_name: adicciones_postgres
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: adicciones
|
||||
MYSQL_USER: user
|
||||
MYSQL_PASSWORD: pass
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: adicciones
|
||||
ports:
|
||||
- "3307:3306"
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- ./mysql_data:/var/lib/mysql
|
||||
- ./pg_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
136
Obtener-Datos-1/importador.py
Normal file
136
Obtener-Datos-1/importador.py
Normal file
@ -0,0 +1,136 @@
|
||||
import os
|
||||
import psycopg2
|
||||
import csv
|
||||
import chardet
|
||||
import re
|
||||
import json
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CONFIGURACIÓN DE CONEXIÓN
|
||||
# ------------------------------------------------------------
|
||||
DB_CONFIG = {
|
||||
"host": "localhost",
|
||||
"port": 5433,
|
||||
"user": "postgres",
|
||||
"password": "postgres",
|
||||
"dbname": "adicciones"
|
||||
}
|
||||
|
||||
DATASETS_DIR = "datasets"
|
||||
OUTPUT_FILE = "columnas_info.json"
|
||||
|
||||
|
||||
def sanitize_name(name: str) -> str:
|
||||
"""Limpia nombres de columnas para SQL."""
|
||||
name = name.strip().lower()
|
||||
name = re.sub(r"[^a-z0-9_]+", "_", name)
|
||||
return name[:50]
|
||||
|
||||
|
||||
def detect_encoding(file_path):
|
||||
"""Detecta codificación del archivo con chardet."""
|
||||
with open(file_path, 'rb') as f:
|
||||
raw = f.read(10000)
|
||||
result = chardet.detect(raw)
|
||||
return result['encoding'] or 'utf-8'
|
||||
|
||||
|
||||
def normalize_encoding(enc: str) -> str:
|
||||
"""Convierte nombres de encoding a formato válido para PostgreSQL."""
|
||||
if not enc:
|
||||
return "UTF8"
|
||||
enc = enc.upper().replace("-", "").replace("_", "")
|
||||
if "UTF" in enc:
|
||||
return "UTF8"
|
||||
if "LATIN" in enc or "ISO88591" in enc:
|
||||
return "LATIN1"
|
||||
if "1252" in enc or "WINDOWS" in enc:
|
||||
return "WIN1252"
|
||||
return "UTF8" # fallback por defecto
|
||||
|
||||
|
||||
def detect_delimiter(file_path, encoding):
|
||||
"""Detecta el delimitador probable."""
|
||||
with open(file_path, 'r', encoding=encoding, errors='ignore') as f:
|
||||
sample = f.read(4096)
|
||||
try:
|
||||
dialect = csv.Sniffer().sniff(sample, delimiters=";,|\t")
|
||||
return dialect.delimiter
|
||||
except csv.Error:
|
||||
if ";" in sample:
|
||||
return ";"
|
||||
elif "\t" in sample:
|
||||
return "\t"
|
||||
elif "|" in sample:
|
||||
return "|"
|
||||
else:
|
||||
return ","
|
||||
|
||||
|
||||
def main():
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
cur = conn.cursor()
|
||||
columnas_info = {}
|
||||
|
||||
for filename in os.listdir(DATASETS_DIR):
|
||||
if not filename.endswith(".csv"):
|
||||
continue
|
||||
|
||||
path = os.path.join(DATASETS_DIR, filename)
|
||||
table_name = sanitize_name(os.path.splitext(filename)[0])
|
||||
|
||||
print(f"\n📊 Procesando: {filename} → tabla '{table_name}'")
|
||||
|
||||
encoding_detected = detect_encoding(path)
|
||||
pg_encoding = normalize_encoding(encoding_detected)
|
||||
delimiter = detect_delimiter(path, encoding_detected)
|
||||
|
||||
print(f" → detectado: delimitador '{delimiter}' | codificación '{encoding_detected}' (→ {pg_encoding})")
|
||||
|
||||
# Leer encabezados manualmente
|
||||
with open(path, 'r', encoding=encoding_detected, errors='ignore') as f:
|
||||
reader = csv.reader(f, delimiter=delimiter)
|
||||
headers = next(reader)
|
||||
headers = [sanitize_name(h) for h in headers]
|
||||
|
||||
columnas_info[table_name] = headers
|
||||
|
||||
# Crear tabla limpia
|
||||
cur.execute(f"DROP TABLE IF EXISTS {table_name} CASCADE;")
|
||||
cols_sql = ", ".join([f"{col} TEXT" for col in headers])
|
||||
cur.execute(f"CREATE TABLE {table_name} ({cols_sql});")
|
||||
conn.commit()
|
||||
|
||||
# Intentar importar con encoding normalizado
|
||||
try:
|
||||
with open(path, 'r', encoding=encoding_detected, errors='ignore') as f:
|
||||
next(f) # saltar encabezado
|
||||
cur.copy_expert(
|
||||
sql=f"COPY {table_name} ({', '.join(headers)}) FROM STDIN WITH (FORMAT CSV, DELIMITER '{delimiter}', NULL '', HEADER FALSE, ENCODING '{pg_encoding}');",
|
||||
file=f
|
||||
)
|
||||
conn.commit()
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error al copiar con '{pg_encoding}': {e}")
|
||||
print(" → Reintentando con ENCODING 'UTF8'")
|
||||
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
next(f)
|
||||
cur.copy_expert(
|
||||
sql=f"COPY {table_name} ({', '.join(headers)}) FROM STDIN WITH (FORMAT CSV, DELIMITER '{delimiter}', NULL '', HEADER FALSE, ENCODING 'UTF8');",
|
||||
file=f
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
print(f"✅ Tabla '{table_name}' importada correctamente ({len(headers)} columnas).")
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(columnas_info, f, indent=4, ensure_ascii=False)
|
||||
|
||||
print(f"\n✅ Importación completada. Estructuras guardadas en '{OUTPUT_FILE}'")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,2 +1,3 @@
|
||||
pandas
|
||||
mysql-connector-python
|
||||
psycopg2-binary
|
||||
chardet
|
||||
Loading…
Reference in New Issue
Block a user