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:
diqueran 2025-10-28 15:41:24 +01:00
parent 6ef0203a7d
commit a24ba9b4d1
5 changed files with 210 additions and 12 deletions

58
Obtener-Datos-1/README.md Normal file
View 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.*

View File

@ -26,7 +26,11 @@
"prov" "prov"
], ],
"condenas_sexo_localidad": [ "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": [ "registro_prohibidos_juego": [
"a_o", "a_o",

View File

@ -1,19 +1,18 @@
services: services:
mysql: postgres:
image: mysql:8.0 image: postgres:15
container_name: adicciones_db container_name: adicciones_postgres
restart: always restart: always
environment: environment:
MYSQL_ROOT_PASSWORD: root POSTGRES_USER: postgres
MYSQL_DATABASE: adicciones POSTGRES_PASSWORD: postgres
MYSQL_USER: user POSTGRES_DB: adicciones
MYSQL_PASSWORD: pass
ports: ports:
- "3307:3306" - "5433:5432"
volumes: volumes:
- ./mysql_data:/var/lib/mysql - ./pg_data:/var/lib/postgresql/data
healthcheck: healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5

View 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()

View File

@ -1,2 +1,3 @@
pandas pandas
mysql-connector-python psycopg2-binary
chardet