from flask import Flask, render_template, request, redirect from apscheduler.schedulers.background import BackgroundScheduler from datetime import datetime import feedparser import hashlib import re import mysql.connector app = Flask(__name__) DB_CONFIG = { 'host': 'localhost', 'user': 'x', 'password': 'x', 'database': 'noticiasrss' } # Página principal: muestra los feeds y el formulario con selects de categoría, continente y país @app.route('/') def index(): conn = None try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() # Feeds con país/categoría (incluye nombres) cursor.execute(""" SELECT f.id, f.nombre, f.url, f.categoria_id, f.pais_id, f.activo, c.nombre, p.nombre FROM feeds f LEFT JOIN categorias_estandar c ON f.categoria_id = c.id LEFT JOIN paises p ON f.pais_id = p.id """) feeds = cursor.fetchall() # Categorías, continentes y países para los selects cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre") categorias = cursor.fetchall() cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre") continentes = cursor.fetchall() cursor.execute("SELECT id, nombre, continente_id FROM paises ORDER BY nombre") paises = cursor.fetchall() except mysql.connector.Error as db_err: app.logger.error(f"[DB ERROR] Al leer feeds/categorías/países: {db_err}", exc_info=True) feeds, categorias, continentes, paises = [], [], [], [] finally: if conn: conn.close() return render_template("index.html", feeds=feeds, categorias=categorias, continentes=continentes, paises=paises) # Añadir feed: ahora requiere país y categoría @app.route('/add', methods=['POST']) def add_feed(): nombre = request.form.get('nombre') url = request.form.get('url') categoria_id = request.form.get('categoria_id') pais_id = request.form.get('pais_id') try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() cursor.execute( "INSERT INTO feeds (nombre, url, categoria_id, pais_id) VALUES (%s, %s, %s, %s)", (nombre, url, categoria_id, pais_id) ) conn.commit() except mysql.connector.Error as db_err: app.logger.error(f"[DB ERROR] Al agregar feed: {db_err}", exc_info=True) finally: if conn: conn.close() return redirect('/') # Mostrar noticias, con filtros por categoría, continente y país @app.route('/noticias') def show_noticias(): conn = None noticias = [] categorias = [] continentes = [] paises = [] # Lee los filtros enviados por GET cat_id = request.args.get('categoria_id') cont_id = request.args.get('continente_id') pais_id = request.args.get('pais_id') try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre") categorias = cursor.fetchall() cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre") continentes = cursor.fetchall() # Filtra países si hay continente seleccionado, si no trae todos if cont_id: cursor.execute("SELECT id, nombre, continente_id FROM paises WHERE continente_id = %s ORDER BY nombre", (cont_id,)) else: cursor.execute("SELECT id, nombre, continente_id FROM paises ORDER BY nombre") paises = cursor.fetchall() sql = """ SELECT n.fecha, n.titulo, n.resumen, n.url, n.imagen_url, c.nombre AS categoria, p.nombre AS pais, co.nombre AS continente FROM noticias n LEFT JOIN categorias_estandar c ON n.categoria_id = c.id LEFT JOIN paises p ON n.pais_id = p.id LEFT JOIN continentes co ON p.continente_id = co.id WHERE 1=1 """ params = [] if cat_id: sql += " AND n.categoria_id = %s" params.append(cat_id) if pais_id: sql += " AND n.pais_id = %s" params.append(pais_id) elif cont_id: sql += " AND p.continente_id = %s" params.append(cont_id) sql += " ORDER BY n.fecha DESC LIMIT 50" cursor.execute(sql, params) noticias = cursor.fetchall() except mysql.connector.Error as db_err: app.logger.error(f"[DB ERROR] Al leer noticias: {db_err}", exc_info=True) finally: if conn: conn.close() return render_template( 'noticias.html', noticias=noticias, categorias=categorias, continentes=continentes, paises=paises, cat_id=int(cat_id) if cat_id else None, cont_id=int(cont_id) if cont_id else None, pais_id=int(pais_id) if pais_id else None ) def fetch_and_store(): conn = None try: conn = mysql.connector.connect(**DB_CONFIG) cursor = conn.cursor() # Leer feeds activos con país/categoría cursor.execute("SELECT url, categoria_id, pais_id FROM feeds WHERE activo = TRUE") feeds = cursor.fetchall() except mysql.connector.Error as db_err: app.logger.error(f"[DB ERROR] No se pudo conectar o leer feeds: {db_err}", exc_info=True) return for rss_url, categoria_id, pais_id in feeds: try: app.logger.info(f"Procesando feed: {rss_url} [{categoria_id}] [{pais_id}]") parsed = feedparser.parse(rss_url) except Exception as e: app.logger.error(f"[PARSE ERROR] Al parsear {rss_url}: {e}", exc_info=True) continue if getattr(parsed, 'bozo', False): bozo_exc = getattr(parsed, 'bozo_exception', 'Unknown') app.logger.warning(f"[BOZO] Feed mal formado: {rss_url} - {bozo_exc}") continue for entry in parsed.entries: link = entry.get('link') or entry.get('id') if not link: links_list = entry.get('links', []) if isinstance(links_list, list) and links_list: href = next((l.get('href') for l in links_list if l.get('href')), None) link = href if not link: app.logger.error(f"[ENTRY ERROR] Entrada sin link en feed {rss_url}, salto entrada.") continue try: noticia_id = hashlib.md5(link.encode()).hexdigest() titulo = entry.get('title', '') resumen = entry.get('summary', '') imagen_url = '' fecha = None if 'media_content' in entry: imagen_url = entry.media_content[0].get('url', '') else: img = re.search(r'