Cambio de rutas: ahora portada muestra noticias y gestión de feeds pasa a /feeds. Mejoras en navegación y backup.

This commit is contained in:
jlimolina 2025-05-26 17:50:55 +02:00
parent b3dc6a4ac2
commit d7cabbb2c6
5 changed files with 222 additions and 61 deletions

206
app.py
View file

@ -1,10 +1,12 @@
from flask import Flask, render_template, request, redirect
from flask import Flask, render_template, request, redirect, url_for, Response
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime
import feedparser
import hashlib
import re
import mysql.connector
import csv
from io import StringIO
app = Flask(__name__)
@ -15,61 +17,11 @@ DB_CONFIG = {
'database': 'noticiasrss'
}
# Página principal: muestra los feeds y el formulario con selects de categoría, continente y país
# ======================================
# Página principal: últimas noticias
# ======================================
@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():
def home():
conn = None
noticias = []
categorias = []
@ -131,6 +83,150 @@ def show_noticias():
pais_id=int(pais_id) if pais_id else None
)
# ======================================
# Gestión de feeds en /feeds
# ======================================
@app.route('/feeds')
def feeds():
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
@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(url_for('feeds'))
# Editar feed
@app.route('/edit/<int:feed_id>', methods=['GET', 'POST'])
def edit_feed(feed_id):
conn = None
try:
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor(dictionary=True)
if request.method == 'POST':
nombre = request.form.get('nombre')
url_feed = request.form.get('url')
categoria_id = request.form.get('categoria_id')
pais_id = request.form.get('pais_id')
activo = 1 if request.form.get('activo') == 'on' else 0
cursor.execute(
"UPDATE feeds SET nombre=%s, url=%s, categoria_id=%s, pais_id=%s, activo=%s WHERE id=%s",
(nombre, url_feed, categoria_id, pais_id, activo, feed_id)
)
conn.commit()
return redirect(url_for('feeds'))
# GET: Obtener datos actuales del feed
cursor.execute("SELECT * FROM feeds WHERE id = %s", (feed_id,))
feed = cursor.fetchone()
cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre")
categorias = cursor.fetchall()
cursor.execute("SELECT id, nombre FROM paises ORDER BY nombre")
paises = cursor.fetchall()
except mysql.connector.Error as db_err:
app.logger.error(f"[DB ERROR] Al editar feed: {db_err}", exc_info=True)
feed, categorias, paises = {}, [], []
finally:
if conn:
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):
conn = None
try:
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
cursor.execute("DELETE FROM feeds WHERE id=%s", (feed_id,))
conn.commit()
except mysql.connector.Error as db_err:
app.logger.error(f"[DB ERROR] Al eliminar feed: {db_err}", exc_info=True)
finally:
if conn:
conn.close()
return redirect(url_for('feeds'))
# Backup de feeds a CSV
@app.route('/backup_feeds')
def backup_feeds():
conn = None
try:
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
cursor.execute("""
SELECT f.id, f.nombre, f.url, f.categoria_id, c.nombre AS categoria, f.pais_id, p.nombre AS pais, f.activo
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()
header = [desc[0] for desc in cursor.description]
except mysql.connector.Error as db_err:
app.logger.error(f"[DB ERROR] Al hacer backup de feeds: {db_err}", exc_info=True)
return "Error generando backup.", 500
finally:
if conn:
conn.close()
# CSV en memoria
si = StringIO()
cw = csv.writer(si)
cw.writerow(header)
cw.writerows(feeds)
output = si.getvalue()
si.close()
return Response(
output,
mimetype="text/csv",
headers={"Content-Disposition": "attachment;filename=feeds_backup.csv"}
)
# (Antiguo /noticias, por compatibilidad)
@app.route('/noticias')
def show_noticias():
return home()
def fetch_and_store():
conn = None
try:

50
templates/edit_feed.html Normal file
View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Editar Feed RSS</title>
</head>
<body>
<h1>Editar Feed</h1>
<form method="post">
<label>
Nombre del feed:<br>
<input name="nombre" placeholder="Nombre" value="{{ feed['nombre'] }}" required>
</label>
<br><br>
<label>
URL del RSS:<br>
<input name="url" placeholder="URL" value="{{ feed['url'] }}" required>
</label>
<br><br>
<label>
Categoría:<br>
<select name="categoria_id" required>
<option value="">— Elige categoría —</option>
{% for cat in categorias %}
<option value="{{ cat.id }}" {% if cat.id == feed['categoria_id'] %}selected{% endif %}>{{ cat.nombre }}</option>
{% endfor %}
</select>
</label>
<br><br>
<label>
País:<br>
<select name="pais_id" required>
<option value="">— Elige país —</option>
{% for p in paises %}
<option value="{{ p.id }}" {% if p.id == feed['pais_id'] %}selected{% endif %}>{{ p.nombre }}</option>
{% endfor %}
</select>
</label>
<br><br>
<label>
<input type="checkbox" name="activo" {% if feed['activo'] %}checked{% endif %}>
Activo
</label>
<br><br>
<button type="submit">Guardar cambios</button>
<a href="{{ url_for('index') }}">Cancelar</a>
</form>
</body>
</html>

View file

@ -2,7 +2,7 @@
<html lang="es">
<head>
<meta charset="utf-8">
<title>Mis Feeds RSS</title>
<title>Gestión de Feeds RSS</title>
<script>
// Script para filtrar países según continente elegido
function filtrarPaisesPorContinente() {
@ -27,8 +27,13 @@
</script>
</head>
<body>
<h1>Feeds configurados</h1>
<p><a href="/noticias">📄 Ver últimas noticias</a></p>
<h1>Gestión de Feeds RSS</h1>
<p><a href="/">← Volver a últimas noticias</a></p>
<!-- Enlace de backup de feeds -->
<p>
<a href="/backup_feeds" target="_blank">⬇️ Descargar backup de feeds (CSV)</a>
</p>
<h2>Añadir un nuevo feed</h2>
<form action="/add" method="post">
@ -66,11 +71,17 @@
— Categoría: {{ cat_nom or 'N/A' }}
— País: {{ pais_nom or 'N/A' }}
— Activo: {{ 'Sí' if activo else 'No' }}
<!-- Enlace de edición de feed -->
| <a href="/edit/{{ id }}">Editar</a>
<!-- Enlace para eliminar feed con confirmación -->
| <a href="/delete/{{ id }}" onclick="return confirm('¿Seguro que quieres eliminar este feed?');">Eliminar</a>
</li>
{% else %}
<li>No hay feeds aún.</li>
{% endfor %}
</ul>
<p><a href="/">← Volver a últimas noticias</a></p>
</body>
</html>

View file

@ -27,9 +27,11 @@
</head>
<body>
<h1>Últimas Noticias Recopiladas</h1>
<p><a href="/">← Volver a feeds</a></p>
<form method="get" action="/noticias">
<!-- Enlace de gestión de feeds -->
<p><a href="/feeds">⚙️ Gestionar feeds RSS</a></p>
<form method="get" action="">
<select name="categoria_id">
<option value="">— Categoría —</option>
{% for cid, cnom in categorias %}
@ -45,7 +47,7 @@
<select name="pais_id" id="pais_id">
<option value="">— País —</option>
{% for pid, pnom, contid in paises %}
<option value="{{ pid }}"
<option value="{{ pid }}"
{% if pais_id == pid %}selected{% endif %}
{% if cont_id and contid != cont_id %}style="display:none"{% endif %}>
{{ pnom }}
@ -77,6 +79,8 @@
<li>No hay noticias que mostrar con estos filtros.</li>
{% endfor %}
</ul>
<p><a href="/feeds">← Volver a gestión de feeds</a></p>
</body>
</html>