ranking = FileAttachment("datos/ranking.csv").csv()
toNum = (v) => {
const n = Number(v)
return Number.isFinite(n) ? n : 0
}
normalizar = (v, fallback = "(Sin dato)") => {
if (v === null || v === undefined) return fallback
const t = String(v).trim()
return t === "" ? fallback : t
}
contributors = ranking
.map((d) => ({
user_id: normalizar(d.user_id, ""),
user_name: normalizar(d.user_name),
total_cambios: toNum(d.total_cambios),
primera_actividad: normalizar(d.primera_actividad, ""),
ultima_actividad: normalizar(d.ultima_actividad, ""),
acciones: normalizar(d.acciones, ""),
entity_types: normalizar(d.entity_types, "")
}))
.sort((a, b) => b.total_cambios - a.total_cambios)
totalContribuidores = contributors.length
totalCambios = contributors.reduce((acc, d) => acc + d.total_cambios, 0)
promedioCambios = totalContribuidores ? totalCambios / totalContribuidores : 0
topContribuidor = contributors[0] || null
top20 = contributors.slice(0, 20)Plot.plot({
height: 520,
marginLeft: 220,
x: {label: "Total de cambios"},
y: {label: null},
marks: [
Plot.barX(top20, {
x: "total_cambios",
y: "user_name",
fill: "#0d6efd",
sort: {y: "x", reverse: true},
tip: true
}),
Plot.text(top20, {
x: "total_cambios",
y: "user_name",
text: (d) => `${d.total_cambios}`,
dx: 8,
textAnchor: "start",
fill: "#212529"
})
]
})textoFiltro = (filtrosTabla.nombre || "").trim().toLowerCase()
contributorsFiltrados = contributors.filter((d) => {
const matchNombre = !textoFiltro ||
d.user_name.toLowerCase().includes(textoFiltro) ||
d.user_id.toLowerCase().includes(textoFiltro)
const matchCambios = d.total_cambios >= filtrosTabla.minCambios
return matchNombre && matchCambios
})Inputs.table(
contributorsFiltrados.map((d) => ({
user_name: d.user_name,
user_id: d.user_id,
total_cambios: d.total_cambios,
primera_actividad: d.primera_actividad,
ultima_actividad: d.ultima_actividad,
acciones: d.acciones,
entity_types: d.entity_types
})),
{
columns: ["user_name", "user_id", "total_cambios", "primera_actividad", "ultima_actividad", "acciones", "entity_types"],
sort: "total_cambios",
reverse: true
}
)