feat: alertas de picos, grafica de menciones por dia y estabilidad del traductor

- Backend: endpoint /api/entities/mentions (series por dia para N conceptos)
- Backend: tabla alertas + escaner de picos automatico + endpoints (list/read/scan)
- Frontend: pagina Evolucion con grafica SVG multi-concepto configurable
- Frontend: pagina Alertas con badge de notificaciones en la barra
- Traductor: memoria acotada (sub-lotes, batch 32, cuerpo max 12000 chars)
- Traductor: prioridad baja en CPU (cpu_shares 512) y tope de 2 nucleos x worker
- Escalado a 3 workers de traduccion (2x translator + translator-gpu)
This commit is contained in:
jlimolina 2026-08-11 04:18:37 +02:00
parent eff656adea
commit 3991237685
11 changed files with 988 additions and 28 deletions

View file

@ -76,6 +76,27 @@ func initDB() {
ON CONFLICT (key) DO NOTHING
`)
// Crear tabla de alertas si no existe
_, err = db.GetPool().Exec(ctx, `
CREATE TABLE IF NOT EXISTS alertas (
id SERIAL PRIMARY KEY,
valor VARCHAR(255) NOT NULL,
tipo VARCHAR(32) NOT NULL,
periodo DATE NOT NULL,
hits INT NOT NULL,
baseline DOUBLE PRECISION NOT NULL,
ratio DOUBLE PRECISION NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'nueva',
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(valor, tipo, periodo)
)
`)
if err != nil {
log.Printf("Warning: Could not create alertas table: %v", err)
} else {
log.Println("Table alertas ready")
}
// Crear tabla de remote_workers si no existe
_, err = db.GetPool().Exec(ctx, `
CREATE TABLE IF NOT EXISTS remote_workers (
@ -175,6 +196,11 @@ func main() {
api.GET("/entities", handlers.GetEntities)
api.GET("/entities/news", handlers.GetEntityNews)
api.GET("/entities/mentions", handlers.GetEntityMentions)
api.GET("/alerts", handlers.GetAlertas)
api.POST("/alerts/:id/read", middleware.AuthRequired(), handlers.MarkAlertaRead)
api.POST("/alerts/read-all", middleware.AuthRequired(), handlers.MarkAllAlertasRead)
api.GET("/stats", handlers.GetStats)
@ -195,6 +221,7 @@ func main() {
admin.POST("/users/:id/promote", handlers.PromoteUser)
admin.POST("/users/:id/demote", handlers.DemoteUser)
admin.POST("/reset-db", handlers.ResetDatabase)
admin.POST("/alerts/scan", handlers.ScanAlertasAdmin)
admin.GET("/workers/status", handlers.GetWorkerStatus)
admin.GET("/workers/stats", handlers.GetTranslationStats)
admin.POST("/workers/config", handlers.SetWorkerConfig)
@ -231,6 +258,7 @@ func main() {
}()
handlers.StartJobAssigner()
handlers.StartAlertScanner()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)