File size: 3,106 Bytes
fdb7c58 20faa99 fdb7c58 d088c21 fdb7c58 d088c21 fdb7c58 2b529d5 fdb7c58 2b529d5 fdb7c58 66f319d fdb7c58 66f319d fdb7c58 d088c21 fdb7c58 66f319d fdb7c58 20faa99 fdb7c58 d088c21 66f319d 20faa99 fdb7c58 2b529d5 fdb7c58 2b529d5 fdb7c58 2b529d5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
import {
ModelData,
ProviderInfo,
CalendarData,
Activity,
} from "../types/heatmap";
export const generateCalendarData = (
modelData: ModelData[],
providers: ProviderInfo[]
): CalendarData => {
const data: Record<string, Activity[]> = Object.fromEntries(
providers.map((provider) => [provider.authors[0], []]),
);
const today = new Date();
// Calculate the first date to display GitHub-style:
// 1. Go back 52 weeks (364 days) from today so we cover the last year.
// 2. Shift further back to the previous Sunday (if necessary) so that
// the first column of the heatmap always begins on a Sunday.
const startDate = new Date(today);
startDate.setDate(startDate.getDate() - 364);
const dayOfWeek = startDate.getDay();
startDate.setDate(startDate.getDate() - dayOfWeek);
// create a map to store counts and item names for each provider and date
const countMap: Record<string, Record<string, number>> = {};
const itemsMap: Record<string, Record<string, string[]>> = {};
modelData.forEach((item) => {
const dateString = item.createdAt.split("T")[0];
providers.forEach(({ authors }) => {
if (authors.some((author) => item.id.startsWith(author))) {
const providerKey = authors[0];
// Initialize maps if needed
countMap[providerKey] = countMap[providerKey] || {};
itemsMap[providerKey] = itemsMap[providerKey] || {};
// Increment count
countMap[providerKey][dateString] =
(countMap[providerKey][dateString] || 0) + 1;
// Add item name to the list
itemsMap[providerKey][dateString] = itemsMap[providerKey][dateString] || [];
itemsMap[providerKey][dateString].push(item.id);
}
});
});
// fill in with 0s for days with no activity
for (let d = new Date(startDate); d <= today; d.setDate(d.getDate() + 1)) {
const dateString = d.toISOString().split("T")[0];
providers.forEach(({ authors }) => {
const providerKey = authors[0];
const count = countMap[providerKey]?.[dateString] || 0;
const items = itemsMap[providerKey]?.[dateString] || [];
data[providerKey].push({
date: dateString,
count,
level: 0,
items
});
});
}
// calculate average counts for each provider
const avgCounts = Object.fromEntries(
Object.entries(data).map(([provider, days]) => [
provider,
days.reduce((sum, day) => sum + day.count, 0) / days.length || 0,
]),
);
const calculateLevel = (count: number, avgCount: number): number => {
if (count === 0) {
return 0;
}
if (count <= avgCount * 0.5) {
return 1;
}
if (count <= avgCount) {
return 2;
}
if (count <= avgCount * 1.5) {
return 3;
}
return 4;
};
// assign levels based on count relative to average
Object.entries(data).forEach(([provider, days]) => {
const avgCount = avgCounts[provider];
for (const day of days) {
day.level = calculateLevel(day.count, avgCount);
}
});
return data;
};
|