Añadida restauración de feeds desde CSV, mejoras en UI, y campo descripción en feeds
This commit is contained in:
parent
d7cabbb2c6
commit
4ca48836c9
6 changed files with 298 additions and 182 deletions
68
app.py
68
app.py
|
|
@ -27,7 +27,6 @@ def home():
|
||||||
categorias = []
|
categorias = []
|
||||||
continentes = []
|
continentes = []
|
||||||
paises = []
|
paises = []
|
||||||
# Lee los filtros enviados por GET
|
|
||||||
cat_id = request.args.get('categoria_id')
|
cat_id = request.args.get('categoria_id')
|
||||||
cont_id = request.args.get('continente_id')
|
cont_id = request.args.get('continente_id')
|
||||||
pais_id = request.args.get('pais_id')
|
pais_id = request.args.get('pais_id')
|
||||||
|
|
@ -38,7 +37,6 @@ def home():
|
||||||
categorias = cursor.fetchall()
|
categorias = cursor.fetchall()
|
||||||
cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre")
|
cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre")
|
||||||
continentes = cursor.fetchall()
|
continentes = cursor.fetchall()
|
||||||
# Filtra países si hay continente seleccionado, si no trae todos
|
|
||||||
if cont_id:
|
if cont_id:
|
||||||
cursor.execute("SELECT id, nombre, continente_id FROM paises WHERE continente_id = %s ORDER BY nombre", (cont_id,))
|
cursor.execute("SELECT id, nombre, continente_id FROM paises WHERE continente_id = %s ORDER BY nombre", (cont_id,))
|
||||||
else:
|
else:
|
||||||
|
|
@ -92,15 +90,14 @@ def feeds():
|
||||||
try:
|
try:
|
||||||
conn = mysql.connector.connect(**DB_CONFIG)
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
# Feeds con país/categoría (incluye nombres)
|
# Feeds con descripción
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT f.id, f.nombre, f.url, f.categoria_id, f.pais_id, f.activo, c.nombre, p.nombre
|
SELECT f.id, f.nombre, f.descripcion, f.url, f.categoria_id, f.pais_id, f.activo, c.nombre, p.nombre
|
||||||
FROM feeds f
|
FROM feeds f
|
||||||
LEFT JOIN categorias_estandar c ON f.categoria_id = c.id
|
LEFT JOIN categorias_estandar c ON f.categoria_id = c.id
|
||||||
LEFT JOIN paises p ON f.pais_id = p.id
|
LEFT JOIN paises p ON f.pais_id = p.id
|
||||||
""")
|
""")
|
||||||
feeds = cursor.fetchall()
|
feeds = cursor.fetchall()
|
||||||
# Categorías, continentes y países para los selects
|
|
||||||
cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre")
|
cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre")
|
||||||
categorias = cursor.fetchall()
|
categorias = cursor.fetchall()
|
||||||
cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre")
|
cursor.execute("SELECT id, nombre FROM continentes ORDER BY nombre")
|
||||||
|
|
@ -119,6 +116,7 @@ def feeds():
|
||||||
@app.route('/add', methods=['POST'])
|
@app.route('/add', methods=['POST'])
|
||||||
def add_feed():
|
def add_feed():
|
||||||
nombre = request.form.get('nombre')
|
nombre = request.form.get('nombre')
|
||||||
|
descripcion = request.form.get('descripcion')
|
||||||
url = request.form.get('url')
|
url = request.form.get('url')
|
||||||
categoria_id = request.form.get('categoria_id')
|
categoria_id = request.form.get('categoria_id')
|
||||||
pais_id = request.form.get('pais_id')
|
pais_id = request.form.get('pais_id')
|
||||||
|
|
@ -126,8 +124,8 @@ def add_feed():
|
||||||
conn = mysql.connector.connect(**DB_CONFIG)
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO feeds (nombre, url, categoria_id, pais_id) VALUES (%s, %s, %s, %s)",
|
"INSERT INTO feeds (nombre, descripcion, url, categoria_id, pais_id) VALUES (%s, %s, %s, %s, %s)",
|
||||||
(nombre, url, categoria_id, pais_id)
|
(nombre, descripcion, url, categoria_id, pais_id)
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except mysql.connector.Error as db_err:
|
except mysql.connector.Error as db_err:
|
||||||
|
|
@ -146,17 +144,17 @@ def edit_feed(feed_id):
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor(dictionary=True)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
nombre = request.form.get('nombre')
|
nombre = request.form.get('nombre')
|
||||||
|
descripcion = request.form.get('descripcion')
|
||||||
url_feed = request.form.get('url')
|
url_feed = request.form.get('url')
|
||||||
categoria_id = request.form.get('categoria_id')
|
categoria_id = request.form.get('categoria_id')
|
||||||
pais_id = request.form.get('pais_id')
|
pais_id = request.form.get('pais_id')
|
||||||
activo = 1 if request.form.get('activo') == 'on' else 0
|
activo = 1 if request.form.get('activo') == 'on' else 0
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"UPDATE feeds SET nombre=%s, url=%s, categoria_id=%s, pais_id=%s, activo=%s WHERE id=%s",
|
"UPDATE feeds SET nombre=%s, descripcion=%s, url=%s, categoria_id=%s, pais_id=%s, activo=%s WHERE id=%s",
|
||||||
(nombre, url_feed, categoria_id, pais_id, activo, feed_id)
|
(nombre, descripcion, url_feed, categoria_id, pais_id, activo, feed_id)
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return redirect(url_for('feeds'))
|
return redirect(url_for('feeds'))
|
||||||
# GET: Obtener datos actuales del feed
|
|
||||||
cursor.execute("SELECT * FROM feeds WHERE id = %s", (feed_id,))
|
cursor.execute("SELECT * FROM feeds WHERE id = %s", (feed_id,))
|
||||||
feed = cursor.fetchone()
|
feed = cursor.fetchone()
|
||||||
cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre")
|
cursor.execute("SELECT id, nombre FROM categorias_estandar ORDER BY nombre")
|
||||||
|
|
@ -195,7 +193,7 @@ def backup_feeds():
|
||||||
conn = mysql.connector.connect(**DB_CONFIG)
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("""
|
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
|
SELECT f.id, f.nombre, f.descripcion, f.url, f.categoria_id, c.nombre AS categoria, f.pais_id, p.nombre AS pais, f.activo
|
||||||
FROM feeds f
|
FROM feeds f
|
||||||
LEFT JOIN categorias_estandar c ON f.categoria_id = c.id
|
LEFT JOIN categorias_estandar c ON f.categoria_id = c.id
|
||||||
LEFT JOIN paises p ON f.pais_id = p.id
|
LEFT JOIN paises p ON f.pais_id = p.id
|
||||||
|
|
@ -209,7 +207,6 @@ def backup_feeds():
|
||||||
if conn:
|
if conn:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
# CSV en memoria
|
|
||||||
si = StringIO()
|
si = StringIO()
|
||||||
cw = csv.writer(si)
|
cw = csv.writer(si)
|
||||||
cw.writerow(header)
|
cw.writerow(header)
|
||||||
|
|
@ -222,7 +219,51 @@ def backup_feeds():
|
||||||
headers={"Content-Disposition": "attachment;filename=feeds_backup.csv"}
|
headers={"Content-Disposition": "attachment;filename=feeds_backup.csv"}
|
||||||
)
|
)
|
||||||
|
|
||||||
# (Antiguo /noticias, por compatibilidad)
|
# Restaurar feeds desde CSV
|
||||||
|
@app.route('/restore_feeds', methods=['GET', 'POST'])
|
||||||
|
def restore_feeds():
|
||||||
|
msg = ""
|
||||||
|
if request.method == 'POST':
|
||||||
|
file = request.files.get('file')
|
||||||
|
if not file or not file.filename.endswith('.csv'):
|
||||||
|
msg = "Archivo no válido."
|
||||||
|
else:
|
||||||
|
file_stream = StringIO(file.read().decode('utf-8'))
|
||||||
|
reader = csv.DictReader(file_stream)
|
||||||
|
rows = list(reader)
|
||||||
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
n_ok = 0
|
||||||
|
for row in rows:
|
||||||
|
try:
|
||||||
|
# Soporta CSV con o sin columna 'descripcion'
|
||||||
|
descripcion = row.get('descripcion') or ""
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO feeds (nombre, descripcion, url, categoria_id, pais_id, activo)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
nombre=VALUES(nombre),
|
||||||
|
descripcion=VALUES(descripcion),
|
||||||
|
url=VALUES(url),
|
||||||
|
categoria_id=VALUES(categoria_id),
|
||||||
|
pais_id=VALUES(pais_id),
|
||||||
|
activo=VALUES(activo)
|
||||||
|
""", (
|
||||||
|
row['nombre'],
|
||||||
|
descripcion,
|
||||||
|
row['url'],
|
||||||
|
row['categoria_id'],
|
||||||
|
row['pais_id'],
|
||||||
|
int(row['activo'])
|
||||||
|
))
|
||||||
|
n_ok += 1
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Error insertando feed {row}: {e}")
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
msg = f"Feeds restaurados correctamente: {n_ok}"
|
||||||
|
return render_template("restore_feeds.html", msg=msg)
|
||||||
|
|
||||||
@app.route('/noticias')
|
@app.route('/noticias')
|
||||||
def show_noticias():
|
def show_noticias():
|
||||||
return home()
|
return home()
|
||||||
|
|
@ -232,7 +273,6 @@ def fetch_and_store():
|
||||||
try:
|
try:
|
||||||
conn = mysql.connector.connect(**DB_CONFIG)
|
conn = mysql.connector.connect(**DB_CONFIG)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
# Leer feeds activos con país/categoría
|
|
||||||
cursor.execute("SELECT url, categoria_id, pais_id FROM feeds WHERE activo = TRUE")
|
cursor.execute("SELECT url, categoria_id, pais_id FROM feeds WHERE activo = TRUE")
|
||||||
feeds = cursor.fetchall()
|
feeds = cursor.fetchall()
|
||||||
except mysql.connector.Error as db_err:
|
except mysql.connector.Error as db_err:
|
||||||
|
|
|
||||||
17
templates/base.html
Normal file
17
templates/base.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{% block title %}Noticias RSS{% endblock %}</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Inter:400,600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
/* [todo el CSS del ejemplo anterior aquí] */
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -1,50 +1,42 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="es">
|
{% block title %}Editar Feed RSS{% endblock %}
|
||||||
<head>
|
{% block content %}
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Editar Feed RSS</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Editar Feed</h1>
|
<h1>Editar Feed</h1>
|
||||||
<form method="post">
|
<div class="card">
|
||||||
<label>
|
<form method="post" autocomplete="off">
|
||||||
Nombre del feed:<br>
|
<label for="nombre">Nombre del feed</label>
|
||||||
<input name="nombre" placeholder="Nombre" value="{{ feed['nombre'] }}" required>
|
<input id="nombre" name="nombre" type="text" placeholder="Nombre" value="{{ feed['nombre'] }}" required>
|
||||||
</label>
|
|
||||||
<br><br>
|
<label for="descripcion">Descripción</label>
|
||||||
<label>
|
<textarea id="descripcion" name="descripcion" rows="2" placeholder="Breve descripción del feed">{{ feed['descripcion'] or '' }}</textarea>
|
||||||
URL del RSS:<br>
|
|
||||||
<input name="url" placeholder="URL" value="{{ feed['url'] }}" required>
|
<label for="url">URL del RSS</label>
|
||||||
</label>
|
<input id="url" name="url" type="url" placeholder="URL" value="{{ feed['url'] }}" required>
|
||||||
<br><br>
|
|
||||||
<label>
|
<label for="categoria_id">Categoría</label>
|
||||||
Categoría:<br>
|
<select id="categoria_id" name="categoria_id" required>
|
||||||
<select name="categoria_id" required>
|
|
||||||
<option value="">— Elige categoría —</option>
|
<option value="">— Elige categoría —</option>
|
||||||
{% for cat in categorias %}
|
{% for cat in categorias %}
|
||||||
<option value="{{ cat.id }}" {% if cat.id == feed['categoria_id'] %}selected{% endif %}>{{ cat.nombre }}</option>
|
<option value="{{ cat.id }}" {% if cat.id == feed['categoria_id'] %}selected{% endif %}>{{ cat.nombre }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
|
||||||
<br><br>
|
<label for="pais_id">País</label>
|
||||||
<label>
|
<select id="pais_id" name="pais_id" required>
|
||||||
País:<br>
|
|
||||||
<select name="pais_id" required>
|
|
||||||
<option value="">— Elige país —</option>
|
<option value="">— Elige país —</option>
|
||||||
{% for p in paises %}
|
{% for p in paises %}
|
||||||
<option value="{{ p.id }}" {% if p.id == feed['pais_id'] %}selected{% endif %}>{{ p.nombre }}</option>
|
<option value="{{ p.id }}" {% if p.id == feed['pais_id'] %}selected{% endif %}>{{ p.nombre }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
|
||||||
<br><br>
|
<div style="margin: 12px 0;">
|
||||||
<label>
|
<input type="checkbox" id="activo" name="activo" {% if feed['activo'] %}checked{% endif %}>
|
||||||
<input type="checkbox" name="activo" {% if feed['activo'] %}checked{% endif %}>
|
<label for="activo" style="display:inline;">Activo</label>
|
||||||
Activo
|
</div>
|
||||||
</label>
|
|
||||||
<br><br>
|
<button class="btn" type="submit">Guardar cambios</button>
|
||||||
<button type="submit">Guardar cambios</button>
|
<a href="{{ url_for('feeds') }}">Cancelar</a>
|
||||||
<a href="{{ url_for('index') }}">Cancelar</a>
|
</form>
|
||||||
</form>
|
</div>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,95 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="es">
|
{% block title %}Gestión de Feeds RSS{% endblock %}
|
||||||
<head>
|
{% block content %}
|
||||||
<meta charset="utf-8">
|
<h1>Gestión de Feeds RSS</h1>
|
||||||
<title>Gestión de Feeds RSS</title>
|
<a href="/" class="top-link">← Volver a últimas noticias</a>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Añadir un nuevo feed</h2>
|
||||||
|
<form action="/add" method="post" autocomplete="off">
|
||||||
|
<label for="nombre">Nombre del feed</label>
|
||||||
|
<input id="nombre" name="nombre" placeholder="Nombre del feed" required>
|
||||||
|
|
||||||
|
<label for="descripcion">Descripción</label>
|
||||||
|
<textarea id="descripcion" name="descripcion" placeholder="Breve descripción del feed" rows="2"></textarea>
|
||||||
|
|
||||||
|
<label for="url">URL del RSS</label>
|
||||||
|
<input id="url" name="url" placeholder="URL del RSS" required>
|
||||||
|
|
||||||
|
<label for="categoria_id">Categoría</label>
|
||||||
|
<select id="categoria_id" name="categoria_id" required>
|
||||||
|
<option value="">— Elige categoría —</option>
|
||||||
|
{% for cid, cnom in categorias %}
|
||||||
|
<option value="{{ cid }}">{{ cnom }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="continente_id">Continente</label>
|
||||||
|
<select name="continente_id" id="continente_id" onchange="filtrarPaisesPorContinente()">
|
||||||
|
<option value="">— Elige continente —</option>
|
||||||
|
{% for coid, conom in continentes %}
|
||||||
|
<option value="{{ coid }}">{{ conom }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="pais_id">País</label>
|
||||||
|
<select name="pais_id" id="pais_id">
|
||||||
|
<option value="">— N/A —</option>
|
||||||
|
{% for pid, pnom, contid in paises %}
|
||||||
|
<option value="{{ pid }}">{{ pnom }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<button class="btn" type="submit">Añadir</button>
|
||||||
|
<!-- Datos en JSON para el filtro dinámico de países -->
|
||||||
|
<script type="application/json" id="paises-data">{{ paises|tojson }}</script>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Lista de Feeds</h2>
|
||||||
|
<a href="/backup_feeds" target="_blank" class="btn">⬇️ Descargar backup de feeds (CSV)</a>
|
||||||
|
<a href="/restore_feeds" class="btn" style="margin-left:10px;">🔄 Restaurar feeds desde backup</a>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nombre y descripción</th>
|
||||||
|
<th>URL</th>
|
||||||
|
<th>Categoría</th>
|
||||||
|
<th>País</th>
|
||||||
|
<th>Activo</th>
|
||||||
|
<th>Acciones</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for id, nombre, descripcion, url, categoria_id, pais_id, activo, cat_nom, pais_nom in feeds %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>{{ nombre }}</strong>
|
||||||
|
{% if descripcion %}
|
||||||
|
<div style="font-size:0.95em; color:#64748b;">{{ descripcion }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td><a href="{{ url }}" target="_blank">{{ url }}</a></td>
|
||||||
|
<td>{{ cat_nom or 'N/A' }}</td>
|
||||||
|
<td>{{ pais_nom or 'N/A' }}</td>
|
||||||
|
<td>{{ 'Sí' if activo else 'No' }}</td>
|
||||||
|
<td class="actions">
|
||||||
|
<a href="/edit/{{ id }}">Editar</a> |
|
||||||
|
<a href="/delete/{{ id }}" onclick="return confirm('¿Seguro que quieres eliminar este feed?');">Eliminar</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6">No hay feeds aún.</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="/" class="top-link">← Volver a últimas noticias</a>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Script para filtrar países según continente elegido
|
|
||||||
function filtrarPaisesPorContinente() {
|
function filtrarPaisesPorContinente() {
|
||||||
const continenteId = document.getElementById('continente_id').value;
|
const continenteId = document.getElementById('continente_id').value;
|
||||||
const paises = JSON.parse(document.getElementById('paises-data').textContent);
|
const paises = JSON.parse(document.getElementById('paises-data').textContent);
|
||||||
|
|
@ -25,63 +110,5 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
<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">
|
|
||||||
<input name="nombre" placeholder="Nombre del feed" required>
|
|
||||||
<input name="url" placeholder="URL del RSS" required>
|
|
||||||
<select name="categoria_id" required>
|
|
||||||
<option value="">— Elige categoría —</option>
|
|
||||||
{% for cid, cnom in categorias %}
|
|
||||||
<option value="{{ cid }}">{{ cnom }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<select name="continente_id" id="continente_id" onchange="filtrarPaisesPorContinente()">
|
|
||||||
<option value="">— Elige continente —</option>
|
|
||||||
{% for coid, conom in continentes %}
|
|
||||||
<option value="{{ coid }}">{{ conom }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<select name="pais_id" id="pais_id">
|
|
||||||
<option value="">— N/A —</option>
|
|
||||||
{% for pid, pnom, contid in paises %}
|
|
||||||
<option value="{{ pid }}">{{ pnom }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<button type="submit">Añadir</button>
|
|
||||||
<!-- Datos en JSON para el filtro dinámico de países -->
|
|
||||||
<script type="application/json" id="paises-data">{{ paises|tojson }}</script>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h2>Lista de Feeds</h2>
|
|
||||||
<ul>
|
|
||||||
{% for id, nombre, url, categoria_id, pais_id, activo, cat_nom, pais_nom in feeds %}
|
|
||||||
<li>
|
|
||||||
<strong>{{ nombre }}</strong>
|
|
||||||
(<a href="{{ url }}" target="_blank">{{ url }}</a>)
|
|
||||||
— 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>
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,82 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="es">
|
{% block title %}Últimas Noticias RSS{% endblock %}
|
||||||
<head>
|
{% block content %}
|
||||||
<meta charset="utf-8">
|
<h1>Últimas Noticias Recopiladas</h1>
|
||||||
<title>Últimas Noticias RSS</title>
|
<a href="/feeds" class="top-link">⚙️ Gestionar feeds RSS</a>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<form method="get" action="">
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||||||
|
<div style="flex:1;">
|
||||||
|
<label for="categoria_id">Categoría</label>
|
||||||
|
<select name="categoria_id" id="categoria_id">
|
||||||
|
<option value="">— Categoría —</option>
|
||||||
|
{% for cid, cnom in categorias %}
|
||||||
|
<option value="{{ cid }}" {% if cat_id == cid %}selected{% endif %}>{{ cnom }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="flex:1;">
|
||||||
|
<label for="continente_id">Continente</label>
|
||||||
|
<select name="continente_id" id="continente_id" onchange="filtrarPaisesPorContinente()">
|
||||||
|
<option value="">— Continente —</option>
|
||||||
|
{% for coid, conom in continentes %}
|
||||||
|
<option value="{{ coid }}" {% if cont_id == coid %}selected{% endif %}>{{ conom }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="flex:1;">
|
||||||
|
<label for="pais_id">País</label>
|
||||||
|
<select name="pais_id" id="pais_id">
|
||||||
|
<option value="">— País —</option>
|
||||||
|
{% for pid, pnom, contid in paises %}
|
||||||
|
<option value="{{ pid }}"
|
||||||
|
{% if pais_id == pid %}selected{% endif %}
|
||||||
|
{% if cont_id and contid != cont_id %}style="display:none"{% endif %}>
|
||||||
|
{{ pnom }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="align-self: flex-end;">
|
||||||
|
<button class="btn" type="submit">Filtrar</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="application/json" id="paises-data">{{ paises|tojson }}</script>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2 style="margin-top:0;">Noticias recientes</h2>
|
||||||
|
<ul style="list-style:none; padding:0;">
|
||||||
|
{% for fecha, titulo, resumen, url, imagen_url, cat_nom, pais_nom, cont_nom in noticias %}
|
||||||
|
<li style="margin-bottom: 2em; border-bottom: 1px solid #eee; padding-bottom: 1em;">
|
||||||
|
<div style="display: flex; gap:18px;">
|
||||||
|
{% if imagen_url %}
|
||||||
|
<div>
|
||||||
|
<img src="{{ imagen_url }}" alt="img" style="max-width:160px; max-height:100px; border-radius:6px;">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div style="flex:1;">
|
||||||
|
<div style="color:#64748b; font-size:0.97em;"><b>{{ fecha }}</b></div>
|
||||||
|
<a href="{{ url }}" target="_blank"><strong>{{ titulo }}</strong></a><br>
|
||||||
|
<span>{{ resumen|safe }}</span><br>
|
||||||
|
<small style="color:#64748b;">
|
||||||
|
Categoría: {{ cat_nom or 'N/A' }} |
|
||||||
|
País: {{ pais_nom or 'N/A' }} |
|
||||||
|
Continente: {{ cont_nom or 'N/A' }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>No hay noticias que mostrar con estos filtros.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="/feeds" class="top-link">← Volver a gestión de feeds</a>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function filtrarPaisesPorContinente() {
|
function filtrarPaisesPorContinente() {
|
||||||
const continenteId = document.getElementById('continente_id').value;
|
const continenteId = document.getElementById('continente_id').value;
|
||||||
|
|
@ -12,7 +86,7 @@
|
||||||
// Opción N/A siempre presente
|
// Opción N/A siempre presente
|
||||||
const optionNA = document.createElement('option');
|
const optionNA = document.createElement('option');
|
||||||
optionNA.value = '';
|
optionNA.value = '';
|
||||||
optionNA.textContent = '— N/A —';
|
optionNA.textContent = '— País —';
|
||||||
selectPais.appendChild(optionNA);
|
selectPais.appendChild(optionNA);
|
||||||
paises.forEach(([id, nombre, contId]) => {
|
paises.forEach(([id, nombre, contId]) => {
|
||||||
if (!continenteId || contId == continenteId) {
|
if (!continenteId || contId == continenteId) {
|
||||||
|
|
@ -24,63 +98,5 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
<h1>Últimas Noticias Recopiladas</h1>
|
|
||||||
|
|
||||||
<!-- 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 %}
|
|
||||||
<option value="{{ cid }}" {% if cat_id == cid %}selected{% endif %}>{{ cnom }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<select name="continente_id" id="continente_id" onchange="filtrarPaisesPorContinente()">
|
|
||||||
<option value="">— Continente —</option>
|
|
||||||
{% for coid, conom in continentes %}
|
|
||||||
<option value="{{ coid }}" {% if cont_id == coid %}selected{% endif %}>{{ conom }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<select name="pais_id" id="pais_id">
|
|
||||||
<option value="">— País —</option>
|
|
||||||
{% for pid, pnom, contid in paises %}
|
|
||||||
<option value="{{ pid }}"
|
|
||||||
{% if pais_id == pid %}selected{% endif %}
|
|
||||||
{% if cont_id and contid != cont_id %}style="display:none"{% endif %}>
|
|
||||||
{{ pnom }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<button type="submit">Filtrar</button>
|
|
||||||
<script type="application/json" id="paises-data">{{ paises|tojson }}</script>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{% for fecha, titulo, resumen, url, imagen_url, cat_nom, pais_nom, cont_nom in noticias %}
|
|
||||||
<li style="margin-bottom: 2em;">
|
|
||||||
<small><b>{{ fecha }}</b></small><br>
|
|
||||||
<a href="{{ url }}" target="_blank"><strong>{{ titulo }}</strong></a><br>
|
|
||||||
{% if imagen_url %}
|
|
||||||
<img src="{{ imagen_url }}" alt="img" style="max-width:200px; max-height:120px;"><br>
|
|
||||||
{% endif %}
|
|
||||||
<span>{{ resumen|safe }}</span><br>
|
|
||||||
<small>
|
|
||||||
Categoría: {{ cat_nom or 'N/A' }} |
|
|
||||||
País: {{ pais_nom or 'N/A' }} |
|
|
||||||
Continente: {{ cont_nom or 'N/A' }}
|
|
||||||
</small>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
|
||||||
24
templates/restore_feeds.html
Normal file
24
templates/restore_feeds.html
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Restaurar Feeds RSS{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<h1>Restaurar feeds desde backup CSV</h1>
|
||||||
|
<div class="card" style="max-width:500px;margin:auto;">
|
||||||
|
<form method="post" enctype="multipart/form-data" autocomplete="off">
|
||||||
|
<label for="file"><strong>Selecciona el archivo CSV exportado de feeds:</strong></label>
|
||||||
|
<input type="file" name="file" id="file" accept=".csv" required style="margin:10px 0;">
|
||||||
|
<button class="btn" type="submit">Restaurar</button>
|
||||||
|
</form>
|
||||||
|
{% if msg %}
|
||||||
|
<div style="margin:15px 0;">
|
||||||
|
<strong>{{ msg }}</strong>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<p style="font-size:0.92em;color:#64748b;">
|
||||||
|
El archivo debe contener las columnas: <br>
|
||||||
|
<code>id, nombre, [descripcion,] url, categoria_id, categoria, pais_id, pais, activo</code><br>
|
||||||
|
<small>La columna <b>descripcion</b> es opcional.</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="/feeds" class="top-link">← Volver a feeds</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue