El dashboard de investigación: 4 paneles y un botón
Este artículo describe la creación de un dashboard para la investigación de papers de arXiv. El dashboard permite seleccionar queries configurables y visualizar los resultados en cuatro paneles: papers recientes, tendencias por categoría, top autores y keywords frecuentes. Se utiliza React para el frontend, Recharts para la visualización de datos y Material UI para la interfaz. El lector aprenderá cómo construir un dashboard interactivo para analizar datos científicos, incluyendo la implementación de un botón para disparar manualmente la actualización de los datos.
El backend del post anterior dejó cuatro endpoints listos: queries configurables, papers del último snapshot, estadísticas computadas y un trigger manual para el ETL. Lo que faltaba era ponerle cara.
Cuatro paneles, un selector
El dashboard tiene una estructura simple: un dropdown para elegir la query activa, un botón para disparar el ETL, y cuatro paneles que se actualizan según la query seleccionada.
const ArxivDashboard = () => {
const [queries, setQueries] = useState([]);
const [selectedQuery, setSelectedQuery] = useState(null);
const [papers, setPapers] = useState([]);
const [stats, setStats] = useState(null);
const [refreshKey, setRefreshKey] = useState(0);
useEffect(() => {
getArxivQueries().then(r => {
setQueries(r.data);
if (r.data.length > 0) setSelectedQuery(r.data[0].id);
});
}, []);
Cuando el usuario cambia la query en el dropdown, se disparan dos fetches en paralelo — papers y stats — y los paneles se re-renderizan. El refreshKey fuerza el re-fetch después de un snapshot manual.
Panel 1: Papers recientes
Una tabla con título (linkeado al PDF), abstract truncado a 300 caracteres, autores como chips (máximo 3 visibles + contador) y categorías arXiv. Es la vista más útil para un escaneo rápido de qué se publicó.
<TableCell>
<Link href={paper.pdf_url} target="_blank">
{paper.title}
</Link>
</TableCell>
<TableCell>
{paper.authors.slice(0, 3).map(a => (
<Chip key={a} label={a} size="small" />
))}
{paper.authors.length > 3 && (
<Chip label={`+${paper.authors.length - 3}`} size="small" />
)}
</TableCell>
Panel 2: Tendencias por categoría
Un BarChart horizontal de Recharts mostrando cuántos papers hay por categoría arXiv. Permite ver de un vistazo si las publicaciones recientes están más concentradas en un área que en otra.
Panel 3: Top autores
BarChart vertical de los 15 autores más frecuentes en los resultados. Los nombres se truncan a 28 caracteres en el eje para que el gráfico no se deforme. Es una buena forma de detectar quién está publicando activamente en un tema.
Panel 4: Keywords frecuentes
Dual display: chips con conteo para lectura rápida, y un BarChart para comparación visual. Las keywords se extraen de los títulos de los papers, filtradas por una lista de stopwords en inglés y español:
STOPWORDS = {
"the", "a", "an", "of", "for", "in", "on", "with", "to",
"and", "by", "from", "is", "are", "using", "based", "via",
"model", "models", "learning", "data", "approach", "method",
"el", "de", "en", "con", "para", "los", "las", "una", "del",
}
Las stopwords incluyen términos técnicos genéricos como "model" o "learning" que aparecerían en prácticamente todos los papers y no aportan información diferenciadora. Lo que queda son los términos específicos que caracterizan cada nicho de investigación.
El botón de snapshot
Un detalle de UX que resultó más útil de lo esperado: el botón "Actualizar datos" que dispara POST /api/arxiv/snapshot/ desde el frontend.
const handleSnapshot = async () => {
setSnapshotLoading(true);
try {
await triggerArxivSnapshot();
setRefreshKey(k => k + 1); // Fuerza re-fetch de todos los paneles
setAlert({ severity: "success", message: "Datos actualizados." });
} catch (err) {
setAlert({ severity: "error", message: "Error al actualizar." });
} finally {
setSnapshotLoading(false);
}
};
Como el ETL tarda ~20 segundos (3 segundos × queries activas), el botón muestra un spinner y queda deshabilitado mientras corre. Después, el refreshKey invalida todos los paneles y se recargan con datos frescos.
A diferencia del pipeline de ecommerce que corre automáticamente con Cloud Scheduler, los snapshots de arXiv son manuales. El volumen de papers no justifica un cron diario — con actualizar una o dos veces por semana es suficiente para mantener el radar activo.
Integración en el Analytics Hub
El dashboard no vive solo. Se integra en un hub de analytics junto al dashboard de YouTube, usando tabs de MUI:
const AnalyticsHub = () => {
const [tab, setTab] = useState(initialTab);
return (
<Box>
<Tabs value={tab} onChange={(_, v) => setTab(v)}>
<Tab icon={<YouTubeIcon />} label="YouTube" />
<Tab icon={<ArticleIcon />} label="Papers" />
</Tabs>
{tab === 0 && <YouTubeDashboard />}
{tab === 1 && <ArxivDashboard />}
</Box>
);
};
La URL soporta query strings (/analytics?tab=arxiv) para linking directo a una tab. Si mañana agrego un tercer dashboard, es agregar un tab más.
41 tests entre backend y frontend
El proyecto tiene 23 tests de backend y 18 de frontend. Los del backend cubren permisos, deduplicación y formato de respuesta. Los del frontend son más interesantes — testean que recharts renderice los contenedores correctos, que el selector de queries funcione, y que el botón de snapshot maneje errores:
test("muestra alerta de error si el snapshot falla", async () => {
triggerArxivSnapshot.mockRejectedValue(new Error("fail"));
// Click en Actualizar → debe aparecer Alert severity="error"
});
Mockear recharts fue necesario — los componentes de gráficos no renderizan en jsdom, así que los reemplazo por divs con data-testid:
jest.mock("recharts", () => ({
ResponsiveContainer: ({ children }) => <div>{children}</div>,
BarChart: (props) => <div data-testid="bar-chart" />,
// ...
}));
El resultado
El dashboard de arXiv es la herramienta más simple del portafolio, pero una de las más útiles. En 20 segundos tengo un snapshot de lo que se está publicando en mis áreas de interés, quién lo publica, y qué keywords están en auge. Es exactamente el filtro que necesitaba para no perderme en el volumen de publicaciones académicas.
Comparado con el pipeline de ecommerce (R + Cloud Run Jobs + segunda BD + Google Trends + RRF), el de arXiv es deliberadamente minimalista: Django puro, sin dependencias externas más allá de requests, sin scheduling automático. A veces la solución más simple es la correcta.