Object with weekly skip metrics
export async function aggregateWeeklySkipMetrics() {
try {
// First make sure daily metrics are up to date
const dailyMetrics = await aggregateDailySkipMetrics();
const statistics = await getStatistics();
// Create a map to hold weekly metrics
const weeklyMetrics: Record<
string,
{
weekId: string;
startDate: string;
endDate: string;
totalSkips: number;
uniqueTracksSkipped: string[];
skipsByType: Record<string, number>;
skipsByDay: number[];
manualSkips: number;
autoSkips: number;
topSkipHour: number;
}
> = {};
// Process each day's metrics
for (const [dateStr, metrics] of Object.entries(dailyMetrics)) {
try {
// Validate date string before creating Date object
if (!dateStr || typeof dateStr !== "string") {
continue;
}
const date = new Date(dateStr);
// Check if date is valid
if (isNaN(date.getTime())) {
continue;
}
const weekId = getISOWeekIdentifier(date);
// Get start and end dates for this week
const { startDate, endDate } = getWeekStartAndEndDates(date);
// Validate start and end dates
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
continue;
}
const startDateStr = startDate.toISOString().split("T")[0];
const endDateStr = endDate.toISOString().split("T")[0];
// Initialize this week's metrics if needed
if (!weeklyMetrics[weekId]) {
weeklyMetrics[weekId] = {
weekId,
startDate: startDateStr,
endDate: endDateStr,
totalSkips: 0,
uniqueTracksSkipped: [],
skipsByType: {},
skipsByDay: Array(7).fill(0), // Sunday = 0, Saturday = 6
manualSkips: 0,
autoSkips: 0,
topSkipHour: 0,
};
}
// Update metrics
weeklyMetrics[weekId].totalSkips += metrics.tracksSkipped;
// Day of week (0 = Sunday, 6 = Saturday)
const dayOfWeek = date.getDay();
weeklyMetrics[weekId].skipsByDay[dayOfWeek] += metrics.tracksSkipped;
// Add unique tracks
// First ensure metrics.uniqueTracks is an array
const uniqueTracks = Array.isArray(metrics.uniqueTracks)
? metrics.uniqueTracks
: Array.from(metrics.uniqueTracks);
uniqueTracks.forEach((trackId) => {
if (!weeklyMetrics[weekId].uniqueTracksSkipped.includes(trackId)) {
weeklyMetrics[weekId].uniqueTracksSkipped.push(trackId);
}
});
// Add skip types
if (metrics.skipsByType) {
for (const [type, count] of Object.entries(metrics.skipsByType)) {
weeklyMetrics[weekId].skipsByType[type] =
(weeklyMetrics[weekId].skipsByType[type] || 0) + count;
}
}
// Add manual/auto skips
if (metrics.skipsByType?.manual) {
weeklyMetrics[weekId].manualSkips += metrics.skipsByType.manual;
}
if (metrics.skipsByType?.auto) {
weeklyMetrics[weekId].autoSkips += metrics.skipsByType.auto;
}
// Find top skip hour by analyzing hourly skips across all days in the week
const weekHourlySkips = Array(24).fill(0);
for (let hour = 0; hour < 24; hour++) {
// For hourly metrics, we directly check the numbered properties that might exist
// This isn't a proper field of skipsByType, but we're safely accessing it anyway
let hourlySkipCount = 0;
if (metrics.skipsByType) {
// Cast skipsByType to Record<string, number> for string indexing
const skipsByType = metrics.skipsByType as Record<string, number>;
hourlySkipCount = skipsByType[hour.toString()] || 0;
}
weekHourlySkips[hour] += hourlySkipCount;
}
// Update top skip hour if we find a new maximum
const currentMaxHour = weeklyMetrics[weekId].topSkipHour;
const currentMaxCount = weekHourlySkips[currentMaxHour] || 0;
for (let hour = 0; hour < 24; hour++) {
if (weekHourlySkips[hour] > currentMaxCount) {
weeklyMetrics[weekId].topSkipHour = hour;
}
}
} catch (err) {
console.error("Error processing day metrics:", err, {
dateStr,
metrics,
});
}
}
// Store the weekly metrics in a separate file
const weeklyMetricsFilePath = join(
ensureStatisticsDir(),
"weekly_skip_metrics.json",
);
writeJsonSync(weeklyMetricsFilePath, weeklyMetrics, { spaces: 2 });
// Also update the statistics object with the aggregated weekly data
for (const [weekId, metrics] of Object.entries(weeklyMetrics)) {
if (!statistics.weeklyMetrics[weekId]) {
continue; // Skip weeks not in the current statistics object
}
// Update existing weekly metrics
statistics.weeklyMetrics[weekId].tracksSkipped = metrics.totalSkips;
statistics.weeklyMetrics[weekId].mostActiveDay =
metrics.skipsByDay.indexOf(Math.max(...metrics.skipsByDay));
}
// Save updated statistics
await saveStatistics(statistics);
return weeklyMetrics;
} catch (error) {
console.error("Error aggregating weekly skip metrics:", error);
return {};
}
}
Aggregates weekly skip metrics from daily metrics