Corrección importación CSV, robustez en restore_feeds y scripts de instalación para PostgreSQL

This commit is contained in:
jlimolina 2025-05-31 11:23:24 +02:00
parent 72dd972352
commit 0442d3fc0e
6 changed files with 119 additions and 68 deletions

54
app.py
View file

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
"""Flask RSS aggregator — versión PostgreSQL
Cambios principales respecto al original (MySQL):
- mysql.connector psycopg2
- DB_CONFIG con claves PostgreSQL
- Función auxiliar get_conn() para abrir conexiones
- Reemplazo de INSERT IGNORE / ON DUPLICATE KEY UPDATE por ON CONFLICT
(Copyright tuyo 😉, soporte robusto de importación desde CSV)
"""
from flask import Flask, render_template, request, redirect, url_for, Response
@ -32,12 +28,10 @@ DB_CONFIG = {
"password": "x",
}
def get_conn():
"""Devuelve una conexión nueva usando psycopg2 y el diccionario DB_CONFIG."""
return psycopg2.connect(**DB_CONFIG)
MAX_FALLOS = 5 # Número máximo de fallos antes de desactivar el feed
# ======================================
@ -109,7 +103,6 @@ def home():
pais_id=int(pais_id) if pais_id else None,
)
# ======================================
# Gestión de feeds en /feeds
# ======================================
@ -152,7 +145,6 @@ def feeds():
paises=paises,
)
# Añadir feed
@app.route("/add", methods=["POST"])
def add_feed():
@ -180,7 +172,6 @@ def add_feed():
conn.close()
return redirect(url_for("feeds"))
# Editar feed
@app.route("/edit/<int:feed_id>", methods=["GET", "POST"])
def edit_feed(feed_id):
@ -230,7 +221,6 @@ def edit_feed(feed_id):
conn.close()
return render_template("edit_feed.html", feed=feed, categorias=categorias, paises=paises)
# Eliminar feed
@app.route("/delete/<int:feed_id>")
def delete_feed(feed_id):
@ -247,7 +237,6 @@ def delete_feed(feed_id):
conn.close()
return redirect(url_for("feeds"))
# Backup de feeds a CSV
@app.route("/backup_feeds")
def backup_feeds():
@ -285,8 +274,7 @@ def backup_feeds():
headers={"Content-Disposition": "attachment;filename=feeds_backup.csv"},
)
# Restaurar feeds desde CSV
# Restaurar feeds desde CSV (robusto: bool/int/None/códigos)
@app.route("/restore_feeds", methods=["GET", "POST"])
def restore_feeds():
msg = ""
@ -301,8 +289,23 @@ def restore_feeds():
conn = get_conn()
cursor = conn.cursor()
n_ok = 0
for row in rows:
msg_lines = []
for i, row in enumerate(rows, 1):
try:
# -- robusto para activo (admite True/False/1/0/t/f/yes/no/vacío)
activo_val = str(row.get("activo", "")).strip().lower()
if activo_val in ["1", "true", "t", "yes"]:
activo = True
elif activo_val in ["0", "false", "f", "no"]:
activo = False
else:
activo = True # valor por defecto
idioma = row.get("idioma", None)
idioma = idioma.strip() if idioma else None
if idioma == "":
idioma = None
cursor.execute(
"""
INSERT INTO feeds (
@ -320,35 +323,35 @@ def restore_feeds():
fallos = EXCLUDED.fallos;
""",
{
"id": row.get("id"),
"id": int(row.get("id")),
"nombre": row["nombre"],
"descripcion": row.get("descripcion") or "",
"url": row["url"],
"categoria_id": row["categoria_id"],
"pais_id": row["pais_id"],
"idioma": row.get("idioma"),
"activo": bool(int(row["activo"])),
"categoria_id": int(row["categoria_id"]) if row["categoria_id"] else None,
"pais_id": int(row["pais_id"]) if row["pais_id"] else None,
"idioma": idioma,
"activo": activo,
"fallos": int(row.get("fallos", 0)),
},
)
n_ok += 1
except Exception as e:
app.logger.error(f"Error insertando feed {row}: {e}")
app.logger.error(f"Error insertando feed fila {i}: {e}")
msg_lines.append(f"Error en fila {i}: {e}")
conn.commit()
conn.close()
msg = f"Feeds restaurados correctamente: {n_ok}"
if msg_lines:
msg += "<br>" + "<br>".join(msg_lines)
return render_template("restore_feeds.html", msg=msg)
@app.route("/noticias")
def show_noticias():
return home()
# ================================
# Lógica de procesado de feeds con control de fallos
# ================================
def sumar_fallo_feed(cursor, feed_id):
cursor.execute("UPDATE feeds SET fallos = fallos + 1 WHERE id = %s", (feed_id,))
cursor.execute("SELECT fallos FROM feeds WHERE id = %s", (feed_id,))
@ -357,11 +360,9 @@ def sumar_fallo_feed(cursor, feed_id):
cursor.execute("UPDATE feeds SET activo = FALSE WHERE id = %s", (feed_id,))
return fallos
def resetear_fallos_feed(cursor, feed_id):
cursor.execute("UPDATE feeds SET fallos = 0 WHERE id = %s", (feed_id,))
def fetch_and_store():
conn = None
try:
@ -459,7 +460,6 @@ def fetch_and_store():
conn.close()
app.logger.info(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Feeds procesados.")
# ---------------------------------------------------------------------------
# Lanzador de la aplicación + scheduler
# ---------------------------------------------------------------------------