varios cambios esteticos y optimizaciones
This commit is contained in:
parent
e3a99d9604
commit
9a243db633
8 changed files with 64 additions and 105 deletions
5
app.py
5
app.py
|
|
@ -1310,7 +1310,6 @@ def eventos_pais():
|
||||||
|
|
||||||
if pais_id:
|
if pais_id:
|
||||||
with conn.cursor(cursor_factory=extras.DictCursor) as cur:
|
with conn.cursor(cursor_factory=extras.DictCursor) as cur:
|
||||||
# 1) Eventos que tienen al menos una traducción cuya noticia es de ese país
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT
|
SELECT
|
||||||
|
|
@ -1333,7 +1332,6 @@ def eventos_pais():
|
||||||
)
|
)
|
||||||
eventos = cur.fetchall()
|
eventos = cur.fetchall()
|
||||||
|
|
||||||
# 2) Total de eventos distintos para ese país
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
SELECT COUNT(DISTINCT e.id)
|
SELECT COUNT(DISTINCT e.id)
|
||||||
|
|
@ -1346,7 +1344,6 @@ def eventos_pais():
|
||||||
)
|
)
|
||||||
total_eventos = cur.fetchone()[0] if cur.rowcount else 0
|
total_eventos = cur.fetchone()[0] if cur.rowcount else 0
|
||||||
|
|
||||||
# 3) Cargar noticias asociadas a esos eventos (desde traducciones + noticias)
|
|
||||||
if eventos:
|
if eventos:
|
||||||
evento_ids = [e["id"] for e in eventos]
|
evento_ids = [e["id"] for e in eventos]
|
||||||
|
|
||||||
|
|
@ -1381,10 +1378,8 @@ def eventos_pais():
|
||||||
for r in rows:
|
for r in rows:
|
||||||
noticias_por_evento.setdefault(r["evento_id"], []).append(r)
|
noticias_por_evento.setdefault(r["evento_id"], []).append(r)
|
||||||
|
|
||||||
# Nombre del país (todos los eventos en esta vista son del mismo país filtrado)
|
|
||||||
pais_nombre = eventos[0]["pais_nombre"]
|
pais_nombre = eventos[0]["pais_nombre"]
|
||||||
else:
|
else:
|
||||||
# Si no hay eventos, al menos sacamos el nombre del país desde la lista
|
|
||||||
for p in paises:
|
for p in paises:
|
||||||
if p["id"] == int(pais_id):
|
if p["id"] == int(pais_id):
|
||||||
pais_nombre = p["nombre"]
|
pais_nombre = p["nombre"]
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ EVENT_DIST_THRESHOLD = float(os.environ.get("EVENT_DIST_THRESHOLD", "0.25"))
|
||||||
|
|
||||||
EMB_MODEL = os.environ.get(
|
EMB_MODEL = os.environ.get(
|
||||||
"EMB_MODEL",
|
"EMB_MODEL",
|
||||||
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
|
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,12 +43,6 @@ def get_conn():
|
||||||
|
|
||||||
|
|
||||||
def ensure_schema(conn):
|
def ensure_schema(conn):
|
||||||
"""
|
|
||||||
Asumimos que las tablas y columnas (eventos, traducciones.evento_id,
|
|
||||||
eventos_noticias, función/trigger) ya existen por los scripts init-db.
|
|
||||||
Aquí solo nos aseguramos de que existan ciertos índices clave
|
|
||||||
(idempotente).
|
|
||||||
"""
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -66,10 +60,6 @@ def ensure_schema(conn):
|
||||||
|
|
||||||
|
|
||||||
def fetch_pending_traducciones(conn) -> List[int]:
|
def fetch_pending_traducciones(conn) -> List[int]:
|
||||||
"""
|
|
||||||
Traducciones con status 'done', sin evento asignado
|
|
||||||
y que ya tienen embedding en traduccion_embeddings para EMB_MODEL.
|
|
||||||
"""
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -91,10 +81,6 @@ def fetch_pending_traducciones(conn) -> List[int]:
|
||||||
|
|
||||||
|
|
||||||
def fetch_embeddings_for(conn, tr_ids: List[int]) -> Dict[int, np.ndarray]:
|
def fetch_embeddings_for(conn, tr_ids: List[int]) -> Dict[int, np.ndarray]:
|
||||||
"""
|
|
||||||
Devuelve un diccionario {traduccion_id: vector_numpy}
|
|
||||||
leyendo de traduccion_embeddings.embedding para el EMB_MODEL.
|
|
||||||
"""
|
|
||||||
if not tr_ids:
|
if not tr_ids:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
@ -122,10 +108,6 @@ def fetch_embeddings_for(conn, tr_ids: List[int]) -> Dict[int, np.ndarray]:
|
||||||
|
|
||||||
|
|
||||||
def fetch_centroids(conn) -> List[Dict[str, Any]]:
|
def fetch_centroids(conn) -> List[Dict[str, Any]]:
|
||||||
"""
|
|
||||||
Carga todos los centroides actuales desde eventos.
|
|
||||||
Solo usamos campos de clustering: id, centroid, total_traducciones.
|
|
||||||
"""
|
|
||||||
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -142,7 +124,6 @@ def fetch_centroids(conn) -> List[Dict[str, Any]]:
|
||||||
raw = r["centroid"]
|
raw = r["centroid"]
|
||||||
cnt = int(r["total_traducciones"] or 1)
|
cnt = int(r["total_traducciones"] or 1)
|
||||||
if not isinstance(raw, (list, tuple)):
|
if not isinstance(raw, (list, tuple)):
|
||||||
# centroid se almacena como JSONB array → en Python suele llegar como list
|
|
||||||
continue
|
continue
|
||||||
arr = np.array([float(x or 0.0) for x in raw], dtype="float32")
|
arr = np.array([float(x or 0.0) for x in raw], dtype="float32")
|
||||||
if arr.size == 0:
|
if arr.size == 0:
|
||||||
|
|
@ -167,12 +148,6 @@ def cosine_distance(a: np.ndarray, b: np.ndarray) -> float:
|
||||||
|
|
||||||
|
|
||||||
def fetch_traduccion_info(conn, tr_id: int) -> Optional[Dict[str, Any]]:
|
def fetch_traduccion_info(conn, tr_id: int) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
|
||||||
Devuelve info básica para un tr_id:
|
|
||||||
- noticia_id
|
|
||||||
- fecha de la noticia
|
|
||||||
- un título “representativo” para el evento (traducido u original).
|
|
||||||
"""
|
|
||||||
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -199,16 +174,13 @@ def fetch_traduccion_info(conn, tr_id: int) -> Optional[Dict[str, Any]]:
|
||||||
|
|
||||||
|
|
||||||
def _insert_evento_noticia(cur, evento_id: int, info: Dict[str, Any]) -> None:
|
def _insert_evento_noticia(cur, evento_id: int, info: Dict[str, Any]) -> None:
|
||||||
"""
|
|
||||||
Inserta relación en eventos_noticias (idempotente).
|
|
||||||
"""
|
|
||||||
if not info or not info.get("noticia_id"):
|
if not info or not info.get("noticia_id"):
|
||||||
return
|
return
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO eventos_noticias (evento_id, noticia_id, traduccion_id)
|
INSERT INTO eventos_noticias (evento_id, noticia_id, traduccion_id)
|
||||||
VALUES (%s, %s, %s)
|
VALUES (%s, %s, %s)
|
||||||
ON CONFLICT (evento_id, traduccion_id) DO NOTHING;
|
ON CONFLICT (evento_id, noticia_id) DO NOTHING;
|
||||||
""",
|
""",
|
||||||
(evento_id, info["noticia_id"], info["traduccion_id"]),
|
(evento_id, info["noticia_id"], info["traduccion_id"]),
|
||||||
)
|
)
|
||||||
|
|
@ -220,20 +192,11 @@ def assign_to_event(
|
||||||
vec: np.ndarray,
|
vec: np.ndarray,
|
||||||
centroids: List[Dict[str, Any]],
|
centroids: List[Dict[str, Any]],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Asigna una traducción a un evento existente (si distancia <= umbral)
|
|
||||||
o crea un evento nuevo con este vector como centroide.
|
|
||||||
|
|
||||||
Además:
|
|
||||||
- Actualiza fecha_inicio, fecha_fin, n_noticias del evento.
|
|
||||||
- Rellena eventos_noticias (evento_id, noticia_id, traduccion_id).
|
|
||||||
"""
|
|
||||||
if vec is None or vec.size == 0:
|
if vec is None or vec.size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
info = fetch_traduccion_info(conn, tr_id)
|
info = fetch_traduccion_info(conn, tr_id)
|
||||||
|
|
||||||
# Si no hay centroides todavía → primer evento
|
|
||||||
if not centroids:
|
if not centroids:
|
||||||
centroid_list = [float(x) for x in vec.tolist()]
|
centroid_list = [float(x) for x in vec.tolist()]
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
|
|
@ -255,7 +218,6 @@ def assign_to_event(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Fallback mínimo si no hay info de noticia
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO eventos (centroid, total_traducciones)
|
INSERT INTO eventos (centroid, total_traducciones)
|
||||||
|
|
@ -267,19 +229,16 @@ def assign_to_event(
|
||||||
|
|
||||||
new_id = cur.fetchone()[0]
|
new_id = cur.fetchone()[0]
|
||||||
|
|
||||||
# Vincular traducción al evento
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"UPDATE traducciones SET evento_id = %s WHERE id = %s;",
|
"UPDATE traducciones SET evento_id = %s WHERE id = %s;",
|
||||||
(new_id, tr_id),
|
(new_id, tr_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Rellenar tabla de relación
|
|
||||||
_insert_evento_noticia(cur, new_id, info or {})
|
_insert_evento_noticia(cur, new_id, info or {})
|
||||||
|
|
||||||
centroids.append({"id": new_id, "vec": vec.copy(), "n": 1})
|
centroids.append({"id": new_id, "vec": vec.copy(), "n": 1})
|
||||||
return
|
return
|
||||||
|
|
||||||
# Buscar el centroide más cercano
|
|
||||||
best_idx: Optional[int] = None
|
best_idx: Optional[int] = None
|
||||||
best_dist: float = 1.0
|
best_dist: float = 1.0
|
||||||
|
|
||||||
|
|
@ -290,7 +249,6 @@ def assign_to_event(
|
||||||
best_idx = i
|
best_idx = i
|
||||||
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
# Asignar a evento existente si está por debajo del umbral
|
|
||||||
if best_idx is not None and best_dist <= EVENT_DIST_THRESHOLD:
|
if best_idx is not None and best_dist <= EVENT_DIST_THRESHOLD:
|
||||||
c = centroids[best_idx]
|
c = centroids[best_idx]
|
||||||
n_old = c["n"]
|
n_old = c["n"]
|
||||||
|
|
@ -323,7 +281,6 @@ def assign_to_event(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Sin info de fecha: solo actualizamos centroid/contador
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE eventos
|
UPDATE eventos
|
||||||
|
|
@ -334,7 +291,6 @@ def assign_to_event(
|
||||||
(Json(centroid_list), c["id"]),
|
(Json(centroid_list), c["id"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Vincular traducción y relación
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"UPDATE traducciones SET evento_id = %s WHERE id = %s;",
|
"UPDATE traducciones SET evento_id = %s WHERE id = %s;",
|
||||||
(c["id"], tr_id),
|
(c["id"], tr_id),
|
||||||
|
|
@ -343,7 +299,6 @@ def assign_to_event(
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Si no hay evento adecuado → crear uno nuevo
|
|
||||||
centroid_list = [float(x) for x in vec.tolist()]
|
centroid_list = [float(x) for x in vec.tolist()]
|
||||||
|
|
||||||
if info and info.get("fecha"):
|
if info and info.get("fecha"):
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,8 @@ services:
|
||||||
- DB_NAME=${DB_NAME}
|
- DB_NAME=${DB_NAME}
|
||||||
- DB_USER=${DB_USER}
|
- DB_USER=${DB_USER}
|
||||||
- DB_PASS=${DB_PASS}
|
- DB_PASS=${DB_PASS}
|
||||||
|
- EVENT_DIST_THRESHOLD=0.35
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ DB = dict(
|
||||||
|
|
||||||
EMB_MODEL = os.environ.get(
|
EMB_MODEL = os.environ.get(
|
||||||
"EMB_MODEL",
|
"EMB_MODEL",
|
||||||
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
|
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
|
||||||
)
|
)
|
||||||
EMB_BATCH = int(os.environ.get("EMB_BATCH", "128"))
|
EMB_BATCH = int(os.environ.get("EMB_BATCH", "128"))
|
||||||
SLEEP_IDLE = float(os.environ.get("EMB_SLEEP_IDLE", "5.0"))
|
SLEEP_IDLE = float(os.environ.get("EMB_SLEEP_IDLE", "5.0"))
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,27 @@
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
-- =============================================
|
|
||||||
-- Sistema de eventos (clustering incremental)
|
|
||||||
-- =============================================
|
|
||||||
|
|
||||||
-- ---------------------------------------------
|
|
||||||
-- 1. TABLA DE EVENTOS (CLUSTERS)
|
|
||||||
-- ---------------------------------------------
|
|
||||||
CREATE TABLE IF NOT EXISTS eventos (
|
CREATE TABLE IF NOT EXISTS eventos (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
actualizado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
actualizado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
-- Datos "semánticos" del evento (para la web)
|
|
||||||
titulo TEXT,
|
titulo TEXT,
|
||||||
fecha_inicio TIMESTAMPTZ,
|
fecha_inicio TIMESTAMPTZ,
|
||||||
fecha_fin TIMESTAMPTZ,
|
fecha_fin TIMESTAMPTZ,
|
||||||
n_noticias INTEGER NOT NULL DEFAULT 0,
|
n_noticias INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
-- Datos de clustering
|
|
||||||
centroid JSONB NOT NULL,
|
centroid JSONB NOT NULL,
|
||||||
total_traducciones INTEGER NOT NULL DEFAULT 1
|
total_traducciones INTEGER NOT NULL DEFAULT 1
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ---------------------------------------------
|
|
||||||
-- 2. COLUMNA evento_id EN TRADUCCIONES
|
|
||||||
-- ---------------------------------------------
|
|
||||||
ALTER TABLE traducciones
|
ALTER TABLE traducciones
|
||||||
ADD COLUMN IF NOT EXISTS evento_id BIGINT REFERENCES eventos(id);
|
ADD COLUMN IF NOT EXISTS evento_id BIGINT REFERENCES eventos(id);
|
||||||
|
|
||||||
-- ---------------------------------------------
|
|
||||||
-- 3. TABLA RELACIÓN EVENTO <-> NOTICIA <-> TRADUCCIÓN
|
|
||||||
-- (tipos alineados con noticias.id (VARCHAR(32))
|
|
||||||
-- y traducciones.id (INTEGER))
|
|
||||||
-- ---------------------------------------------
|
|
||||||
CREATE TABLE IF NOT EXISTS eventos_noticias (
|
CREATE TABLE IF NOT EXISTS eventos_noticias (
|
||||||
evento_id BIGINT NOT NULL REFERENCES eventos(id) ON DELETE CASCADE,
|
evento_id BIGINT NOT NULL REFERENCES eventos(id) ON DELETE CASCADE,
|
||||||
noticia_id VARCHAR(32) NOT NULL REFERENCES noticias(id) ON DELETE CASCADE,
|
noticia_id VARCHAR(32) NOT NULL REFERENCES noticias(id) ON DELETE CASCADE,
|
||||||
traduccion_id INTEGER NOT NULL REFERENCES traducciones(id) ON DELETE CASCADE,
|
traduccion_id INTEGER NOT NULL REFERENCES traducciones(id) ON DELETE CASCADE,
|
||||||
PRIMARY KEY (evento_id, traduccion_id)
|
PRIMARY KEY (evento_id, noticia_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ---------------------------------------------
|
|
||||||
-- 4. ÍNDICES ÚTILES
|
|
||||||
-- ---------------------------------------------
|
|
||||||
|
|
||||||
-- Consultar traducciones por evento
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_traducciones_evento
|
CREATE INDEX IF NOT EXISTS idx_traducciones_evento
|
||||||
ON traducciones(evento_id);
|
ON traducciones(evento_id);
|
||||||
|
|
||||||
|
|
@ -55,11 +31,9 @@ CREATE INDEX IF NOT EXISTS idx_traducciones_evento_fecha
|
||||||
CREATE INDEX IF NOT EXISTS idx_trad_id
|
CREATE INDEX IF NOT EXISTS idx_trad_id
|
||||||
ON traducciones(id);
|
ON traducciones(id);
|
||||||
|
|
||||||
-- Ordenar eventos por fecha de inicio
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_eventos_fecha_inicio
|
CREATE INDEX IF NOT EXISTS idx_eventos_fecha_inicio
|
||||||
ON eventos (fecha_inicio DESC NULLS LAST);
|
ON eventos (fecha_inicio DESC NULLS LAST);
|
||||||
|
|
||||||
-- Relación evento <-> noticia / traducción
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_eventos_noticias_evento
|
CREATE INDEX IF NOT EXISTS idx_eventos_noticias_evento
|
||||||
ON eventos_noticias (evento_id);
|
ON eventos_noticias (evento_id);
|
||||||
|
|
||||||
|
|
@ -69,9 +43,6 @@ CREATE INDEX IF NOT EXISTS idx_eventos_noticias_noticia
|
||||||
CREATE INDEX IF NOT EXISTS idx_eventos_noticias_traduccion
|
CREATE INDEX IF NOT EXISTS idx_eventos_noticias_traduccion
|
||||||
ON eventos_noticias (traduccion_id);
|
ON eventos_noticias (traduccion_id);
|
||||||
|
|
||||||
-- ---------------------------------------------
|
|
||||||
-- 5. TRIGGER PARA actualizar "actualizado_en"
|
|
||||||
-- ---------------------------------------------
|
|
||||||
CREATE OR REPLACE FUNCTION actualizar_evento_modificado()
|
CREATE OR REPLACE FUNCTION actualizar_evento_modificado()
|
||||||
RETURNS TRIGGER AS $$
|
RETURNS TRIGGER AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
|
||||||
|
|
@ -94,26 +94,6 @@
|
||||||
|
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('click', function(event) {
|
|
||||||
if (event.target.classList.contains('ver-mas-btn')) {
|
|
||||||
const container = event.target.closest('.resumen-container');
|
|
||||||
const corto = container.querySelector('.resumen-corto');
|
|
||||||
const completo = container.querySelector('.resumen-completo');
|
|
||||||
|
|
||||||
if (completo.style.display === 'none' || completo.style.display === '') {
|
|
||||||
corto.style.display = 'none';
|
|
||||||
completo.style.display = 'block';
|
|
||||||
event.target.textContent = 'Ver menos';
|
|
||||||
} else {
|
|
||||||
corto.style.display = 'block';
|
|
||||||
completo.style.display = 'none';
|
|
||||||
event.target.textContent = 'Ver más';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,18 @@
|
||||||
{% for e in eventos %}
|
{% for e in eventos %}
|
||||||
{% set lista = noticias_por_evento.get(e.id) or [] %}
|
{% set lista = noticias_por_evento.get(e.id) or [] %}
|
||||||
{% set primera = lista[0] if lista else None %}
|
{% set primera = lista[0] if lista else None %}
|
||||||
|
{% set titulo_evento = e.titulo %}
|
||||||
|
{% if not titulo_evento %}
|
||||||
|
{% if primera %}
|
||||||
|
{% if primera.titulo_trad %}
|
||||||
|
{% set titulo_evento = primera.titulo_trad %}
|
||||||
|
{% else %}
|
||||||
|
{% set titulo_evento = primera.titulo_orig %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% set titulo_evento = 'Evento' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<li class="noticia-item">
|
<li class="noticia-item">
|
||||||
{% if primera and primera.imagen_url %}
|
{% if primera and primera.imagen_url %}
|
||||||
|
|
@ -70,7 +82,7 @@
|
||||||
|
|
||||||
<div class="noticia-texto">
|
<div class="noticia-texto">
|
||||||
<h3 class="m0">
|
<h3 class="m0">
|
||||||
{{ e.titulo or (primera.titulo_trad or primera.titulo_orig if primera else 'Evento') }}
|
{{ titulo_evento }}
|
||||||
{% if e.n_noticias %}
|
{% if e.n_noticias %}
|
||||||
<span class="badge badge-secondary" title="Número de noticias agrupadas">
|
<span class="badge badge-secondary" title="Número de noticias agrupadas">
|
||||||
{{ e.n_noticias }} noticias
|
{{ e.n_noticias }} noticias
|
||||||
|
|
@ -88,7 +100,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if e.fecha_fin and e.fecha_fin != e.fecha_inicio %}
|
{% if e.fecha_fin and e.fecha_fin != e.fecha_inicio %}
|
||||||
–
|
–
|
||||||
{% if e.fecha_fin is string %}
|
{% if e.fecha_fin is string %}
|
||||||
{{ e.fecha_fin }}
|
{{ e.fecha_fin }}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,8 @@ CHUNK_BY_SENTENCES = _env_bool("CHUNK_BY_SENTENCES", default=True)
|
||||||
CHUNK_MAX_TOKENS = _env_int("CHUNK_MAX_TOKENS", default=900)
|
CHUNK_MAX_TOKENS = _env_int("CHUNK_MAX_TOKENS", default=900)
|
||||||
CHUNK_OVERLAP_SENTS = _env_int("CHUNK_OVERLAP_SENTS", default=0)
|
CHUNK_OVERLAP_SENTS = _env_int("CHUNK_OVERLAP_SENTS", default=0)
|
||||||
|
|
||||||
|
IDENTITY_PAISES_ES = _env_int("IDENTITY_PAISES_ES", default=58)
|
||||||
|
|
||||||
_ABBR = ("Sr", "Sra", "Dr", "Dra", "Ing", "Lic", "pág", "etc")
|
_ABBR = ("Sr", "Sra", "Dr", "Dra", "Ing", "Lic", "pág", "etc")
|
||||||
_ABBR_MARK = "§"
|
_ABBR_MARK = "§"
|
||||||
|
|
||||||
|
|
@ -216,6 +218,8 @@ def ensure_indexes(conn):
|
||||||
|
|
||||||
|
|
||||||
def ensure_pending(conn, lang_to: str, enqueue_limit: int):
|
def ensure_pending(conn, lang_to: str, enqueue_limit: int):
|
||||||
|
if enqueue_limit <= 0:
|
||||||
|
return
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -236,7 +240,44 @@ def ensure_pending(conn, lang_to: str, enqueue_limit: int):
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_identity_spanish(conn, lang_to: str, enqueue_limit: int):
|
||||||
|
lang_to = normalize_lang(lang_to, "es") or "es"
|
||||||
|
if lang_to != "es":
|
||||||
|
return
|
||||||
|
if enqueue_limit <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG.info(
|
||||||
|
"Creando traducciones identidad ES para pais_id=%s (hasta %s noticias)…",
|
||||||
|
IDENTITY_PAISES_ES,
|
||||||
|
enqueue_limit,
|
||||||
|
)
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO traducciones (noticia_id, lang_from, lang_to, titulo_trad, resumen_trad, status)
|
||||||
|
SELECT sub.id, 'es', %s, sub.titulo, sub.resumen, 'done'
|
||||||
|
FROM (
|
||||||
|
SELECT n.id, n.titulo, n.resumen
|
||||||
|
FROM noticias n
|
||||||
|
LEFT JOIN traducciones t
|
||||||
|
ON t.noticia_id = n.id AND t.lang_to = %s
|
||||||
|
WHERE t.id IS NULL
|
||||||
|
AND n.pais_id = %s
|
||||||
|
ORDER BY n.fecha DESC NULLS LAST, n.id
|
||||||
|
LIMIT %s
|
||||||
|
) AS sub;
|
||||||
|
""",
|
||||||
|
(lang_to, lang_to, IDENTITY_PAISES_ES, enqueue_limit),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
def fetch_pending_batch(conn, lang_to: str, batch_size: int):
|
def fetch_pending_batch(conn, lang_to: str, batch_size: int):
|
||||||
|
if batch_size <= 0:
|
||||||
|
return []
|
||||||
|
|
||||||
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
with conn.cursor(cursor_factory=psycopg2.extras.DictCursor) as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"""
|
"""
|
||||||
|
|
@ -865,7 +906,10 @@ def main():
|
||||||
ensure_indexes(conn)
|
ensure_indexes(conn)
|
||||||
for lt in TARGET_LANGS:
|
for lt in TARGET_LANGS:
|
||||||
lt = normalize_lang(lt, "es") or "es"
|
lt = normalize_lang(lt, "es") or "es"
|
||||||
|
|
||||||
|
ensure_identity_spanish(conn, lt, ENQUEUE_MAX)
|
||||||
ensure_pending(conn, lt, ENQUEUE_MAX)
|
ensure_pending(conn, lt, ENQUEUE_MAX)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
rows = fetch_pending_batch(conn, lt, BATCH_SIZE)
|
rows = fetch_pending_batch(conn, lt, BATCH_SIZE)
|
||||||
if not rows:
|
if not rows:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue