2025 / packages /wrapped /domain /aggregate.ts
daqc's picture
Add full-year pagination and UI refinements
657bb2e
import type {
ActivitySnapshot,
PaperStats,
RepoKind,
RepoStats,
StorySlide,
WrappedProfile,
} from "./types";
const MONTH_NAMES = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
export function buildActivitySnapshot(
models: RepoStats[],
datasets: RepoStats[],
spaces: RepoStats[],
papers: PaperStats[],
): ActivitySnapshot {
const all = [...models, ...datasets, ...spaces];
const totalDownloads = all.reduce(
(acc, item) => acc + (item.downloads ?? 0),
0,
);
const totalLikes = all.reduce((acc, item) => acc + (item.likes ?? 0), 0);
const totalRepos = all.length;
const tagFrequency = new Map<string, number>();
all.forEach((repo) => {
repo.tags?.forEach((tag) => {
tagFrequency.set(tag, (tagFrequency.get(tag) ?? 0) + 1);
});
});
const topTags = [...tagFrequency.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, 6)
.map(([tag]) => tag);
const busiestMonth = findBusiestMonth(all);
return {
models,
datasets,
spaces,
papers,
totalDownloads,
totalLikes,
totalRepos,
topTags,
busiestMonth,
};
}
function findBusiestMonth(repos: RepoStats[]): string | undefined {
const monthHits = new Array(12).fill(0);
repos.forEach((repo) => {
const dateString = repo.updatedAt ?? repo.createdAt;
if (!dateString) {
return;
}
const date = new Date(dateString);
if (Number.isNaN(date.valueOf())) {
return;
}
monthHits[date.getUTCMonth()] += 1;
});
const topIndex = monthHits.findIndex(
(count) => count === Math.max(...monthHits),
);
return topIndex >= 0 ? MONTH_NAMES[topIndex] : undefined;
}
export function deriveArchetype(activity: ActivitySnapshot): string {
const modelWeight =
activity.models.length * 2 +
sumMetric(activity.models, "downloads") * 0.000001;
const datasetWeight =
activity.datasets.length * 2 +
sumMetric(activity.datasets, "downloads") * 0.000001;
const spaceWeight =
activity.spaces.length * 2 +
sumMetric(activity.spaces, "likes") * 0.001;
const paperWeight = activity.papers.length * 3;
const weights = [
{ label: "Model Maestro", weight: modelWeight },
{ label: "Dataset Architect", weight: datasetWeight },
{ label: "Space Storyteller", weight: spaceWeight },
{ label: "Research Curator", weight: paperWeight },
];
return (
weights.sort((a, b) => b.weight - a.weight)[0]?.label ?? "HF Explorer"
);
}
function sumMetric(repos: RepoStats[], key: "downloads" | "likes"): number {
return repos.reduce((acc, repo) => acc + (repo[key] ?? 0), 0);
}
export function assignBadges(activity: ActivitySnapshot): string[] {
const badges: string[] = [];
if (activity.totalDownloads > 1_000_000) {
badges.push("Top 1M+ downloads");
}
if (activity.totalLikes > 5_000) {
badges.push("Community favorite");
}
if (activity.models.length >= 10) {
badges.push("Model builder");
}
if (activity.datasets.length >= 5) {
badges.push("Data shaper");
}
if (activity.spaces.length >= 3) {
badges.push("Spaces storyteller");
}
if (activity.busiestMonth) {
badges.push(`Peak month: ${activity.busiestMonth}`);
}
if (activity.topTags.length > 0) {
badges.push(
`Signature tags: ${activity.topTags.slice(0, 3).join(", ")}`,
);
}
return badges;
}
export function buildSlides(params: {
profile: WrappedProfile;
year: number;
activity: ActivitySnapshot;
archetype: string;
badges: string[];
}): StorySlide[] {
const { profile, year, activity, archetype, badges } = params;
const fmt = new Intl.NumberFormat("en-US", { notation: "compact" });
const topModels = sortReposForRanking(activity.models, "model").slice(0, 3);
const topDatasets = sortReposForRanking(activity.datasets, "dataset").slice(
0,
3,
);
const topSpaces = sortReposForRanking(activity.spaces, "space").slice(0, 3);
const slides: StorySlide[] = [
{
id: "intro",
kind: "intro",
title: `Your ${year} Hugging Face Wrapped`,
subtitle: `Hello ${profile.displayName ?? profile.handle}!`,
metrics: [
{
label: "Total repositories",
value: activity.totalRepos.toString(),
accent: "primary",
},
{
label: "Total downloads",
value: fmt.format(activity.totalDownloads),
},
],
highlights: activity.topTags.slice(0, 3),
},
{
id: "summary",
kind: "summary",
title: "Activity pulse",
subtitle: "Across models, datasets, spaces and papers",
metrics: [
{ label: "Models", value: activity.models.length.toString() },
{
label: "Datasets",
value: activity.datasets.length.toString(),
},
{ label: "Spaces", value: activity.spaces.length.toString() },
{ label: "Papers", value: activity.papers.length.toString() },
],
highlights: [
activity.busiestMonth
? `Busiest month: ${activity.busiestMonth}`
: "Consistent contributions all year",
],
},
{
id: "models",
kind: "models",
title: "Top models",
subtitle:
topModels.length > 0
? "Most loved by downloads & likes"
: "No public models yet",
metrics: topModels.map((model) => ({
label: model.name,
value: `${fmt.format(model.downloads ?? 0)} downloads`,
})),
highlights: activity.topTags.slice(0, 2),
},
{
id: "datasets",
kind: "datasets",
title: "Top datasets",
subtitle:
topDatasets.length > 0
? "Fueling experiments everywhere"
: "No public datasets yet",
metrics: topDatasets.map((dataset) => ({
label: dataset.name,
value: `${fmt.format(dataset.downloads ?? 0)} downloads`,
})),
},
{
id: "spaces",
kind: "spaces",
title: "Spaces that sparked engagement",
subtitle:
topSpaces.length > 0
? "Most engaging demos"
: "No public Spaces yet",
metrics: topSpaces.map((space) => ({
label: space.name,
value: `${fmt.format(space.likes ?? 0)} likes`,
})),
},
{
id: "badges",
kind: "badges",
title: "Badges earned",
subtitle:
badges.length > 0
? "Your year at a glance"
: "Fresh start — badges await",
highlights: badges.slice(0, 6),
},
{
id: "archetype",
kind: "archetype",
title: "Your archetype",
subtitle: archetype,
metrics: [
{
label: "Downloads",
value: fmt.format(activity.totalDownloads),
accent: "primary",
},
{
label: "Likes",
value: fmt.format(activity.totalLikes),
},
{
label: "Repos",
value: activity.totalRepos.toString(),
},
],
highlights: activity.topTags.slice(0, 3),
},
{
id: "cta",
kind: "cta",
title: "Share it",
subtitle: "Download the slides or share your Space link",
metrics: [
{
label: "Models",
value: activity.models.length.toString(),
},
{
label: "Datasets",
value: activity.datasets.length.toString(),
},
{
label: "Spaces",
value: activity.spaces.length.toString(),
},
],
},
];
return slides;
}
function sortReposForRanking(repos: RepoStats[], kind: RepoKind): RepoStats[] {
const withKindPriority = [...repos];
const createdAtMs = (repo: RepoStats) => {
const ts = repo.createdAt ?? repo.updatedAt;
const ms = ts ? Date.parse(ts) : Number.NaN;
if (Number.isNaN(ms)) {
return 0;
}
return ms;
};
withKindPriority.sort((a, b) => {
const downloadsDiff = (b.downloads ?? 0) - (a.downloads ?? 0);
const likesDiff = (b.likes ?? 0) - (a.likes ?? 0);
const recencyDiff = createdAtMs(b) - createdAtMs(a);
const nameDiff = a.name.localeCompare(b.name);
if (kind === "space") {
// Spaces focus on engagement (likes), then downloads if present.
if (likesDiff !== 0) {
return likesDiff;
}
if (downloadsDiff !== 0) {
return downloadsDiff;
}
if (recencyDiff !== 0) {
return recencyDiff;
}
return nameDiff;
}
// Models/datasets: downloads first, likes second, recency third.
if (downloadsDiff !== 0) {
return downloadsDiff;
}
if (likesDiff !== 0) {
return likesDiff;
}
if (recencyDiff !== 0) {
return recencyDiff;
}
return nameDiff;
});
return withKindPriority;
}