Promise resolving to a detailed time-based pattern analysis object
// Get time-based skip patterns for visualization
const timePatterns = await analyzeTimeOfDaySkipPatterns();
renderHourlyChart(timePatterns.hourlyDistribution);
highlightPeakHours(timePatterns.peakSkipHours);
export async function analyzeTimeOfDaySkipPatterns(): Promise<{
hourlyDistribution: number[];
hourlyPercentages: number[];
peakSkipHours: number[];
lowSkipHours: number[];
morningSkips: number;
afternoonSkips: number;
eveningSkips: number;
nightSkips: number;
totalSkips: number;
skipsByDayOfWeek: number[];
weekdayVsWeekendSkips: {
weekday: { count: number; percentage: number };
weekend: { count: number; percentage: number };
};
}> {
try {
// Get user data path
const skippedTracksFilePath = join(
getUserDataFolder(),
"skipped_tracks.json",
);
if (!existsSync(skippedTracksFilePath)) {
return getDefaultTimeOfDayResult();
}
const skippedTracks: SkippedTrack[] =
readJsonSync(skippedTracksFilePath, { throws: false }) || [];
// Initialize result structure with explicit types for arrays that were causing errors
const result = {
hourlyDistribution: Array(24).fill(0),
hourlyPercentages: Array(24).fill(0),
peakSkipHours: [] as number[],
lowSkipHours: [] as number[],
morningSkips: 0,
afternoonSkips: 0,
eveningSkips: 0,
nightSkips: 0,
totalSkips: 0,
skipsByDayOfWeek: Array(7).fill(0),
weekdayVsWeekendSkips: {
weekday: { count: 0, percentage: 0 },
weekend: { count: 0, percentage: 0 },
},
};
// Process each skipped track's time data
skippedTracks.forEach((track) => {
// If we have hourly data - use timeOfDay instead of timeOfDayData
if (track.timeOfDay && typeof track.timeOfDay === "object") {
// Convert timeOfDay object to array format
const hourlyData = Array(24).fill(0);
// Process the timeOfDay object which contains string hour keys
Object.entries(track.timeOfDay).forEach(([hourKey, count]) => {
const hour = parseInt(hourKey, 10);
if (!isNaN(hour) && hour >= 0 && hour < 24) {
hourlyData[hour] = count;
}
});
for (let hour = 0; hour < 24; hour++) {
result.hourlyDistribution[hour] += hourlyData[hour];
// Add to time of day groups
if (hour >= 5 && hour <= 11) {
result.morningSkips += hourlyData[hour];
} else if (hour >= 12 && hour <= 17) {
result.afternoonSkips += hourlyData[hour];
} else if (hour >= 18 && hour <= 21) {
result.eveningSkips += hourlyData[hour];
} else {
result.nightSkips += hourlyData[hour];
}
result.totalSkips += hourlyData[hour];
}
}
// Process skip timestamps for day of week analysis
// Use skipEvents timestamps instead of skipTimestamps
if (track.skipEvents && Array.isArray(track.skipEvents)) {
track.skipEvents.forEach((event) => {
const timestamp = new Date(event.timestamp).getTime();
if (!isNaN(timestamp)) {
const date = new Date(timestamp);
const dayOfWeek = date.getDay(); // 0 = Sunday, 6 = Saturday
result.skipsByDayOfWeek[dayOfWeek]++;
// Count weekday vs weekend
if (dayOfWeek === 0 || dayOfWeek === 6) {
result.weekdayVsWeekendSkips.weekend.count++;
} else {
result.weekdayVsWeekendSkips.weekday.count++;
}
}
});
}
});
// Calculate percentages
if (result.totalSkips > 0) {
for (let i = 0; i < 24; i++) {
result.hourlyPercentages[i] =
(result.hourlyDistribution[i] / result.totalSkips) * 100;
}
}
// Find peak and low skip hours (top 3 and bottom 3)
const hourlyRanked = [...result.hourlyDistribution]
.map((count, hour) => ({ hour, count }))
.sort((a, b) => b.count - a.count);
// Set the peak and low hours (using the defined array types)
result.peakSkipHours = hourlyRanked.slice(0, 3).map((h) => h.hour);
result.lowSkipHours = hourlyRanked
.slice(-3)
.reverse()
.map((h) => h.hour);
// Calculate weekday vs weekend percentages
const totalDayOfWeekSkips = result.skipsByDayOfWeek.reduce(
(sum, count) => sum + count,
0,
);
if (totalDayOfWeekSkips > 0) {
result.weekdayVsWeekendSkips.weekday.percentage =
(result.weekdayVsWeekendSkips.weekday.count / totalDayOfWeekSkips) *
100;
result.weekdayVsWeekendSkips.weekend.percentage =
(result.weekdayVsWeekendSkips.weekend.count / totalDayOfWeekSkips) *
100;
}
return result;
} catch (error) {
console.error("Error analyzing time-of-day skip patterns:", error);
return getDefaultTimeOfDayResult();
}
}
Analyzes time-of-day skip patterns
Performs a comprehensive analysis of when users tend to skip tracks throughout the day, week, and across different time periods. This function identifies temporal patterns in listening behavior, such as peak skip hours, day of week trends, and weekday vs. weekend differences.
The analysis includes:
This analysis helps identify optimal listening times and pattern differences that may correlate with user mood, activity level, or environmental factors.