UNPKG

yday

Version:

Git retrospective analysis tool - smart views of recent development work

153 lines (127 loc) 5.12 kB
class Timeline { generate(gitData, timeConfig, showNumbers = false) { const { repos } = gitData; const exceptions = []; const items = repos.map(repo => { const result = this.generateRealPattern(repo, timeConfig, showNumbers); // Track repos that needed to use commit dates if (result.usedCommitDate) { exceptions.push(repo.name); } return { pattern: result.pattern, project: repo.name, commits: repo.commitCount }; }); return { items, exceptions }; } generateRealPattern(repo, timeConfig, showNumbers = false) { // Determine which week to analyze // For Alastair timeline, always use current week to show recent activity let referenceDate = new Date(); // Always use current date for week calculation // Note: We used to use timeConfig.startDate, but this caused issues where // commits would fall outside the calculated week boundaries const dayOfWeek = referenceDate.getDay(); // 0 = Sunday, 1 = Monday, etc. const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; const monday = new Date(referenceDate); monday.setDate(referenceDate.getDate() - daysFromMonday); monday.setHours(0, 0, 0, 0); // Start of day // Create array for each day of the week (Mon-Sun) const weekDays = []; for (let i = 0; i < 7; i++) { const day = new Date(monday); day.setDate(monday.getDate() + i); weekDays.push({ date: day, commits: 0 }); } let usedCommitDate = false; // First pass: try with author dates if (repo.commits && Array.isArray(repo.commits)) { repo.commits.forEach(commit => { if (commit.date) { const authorDate = new Date(commit.date); authorDate.setHours(0, 0, 0, 0); // Normalize to start of day // Find which day of the week this commit belongs to const dayIndex = weekDays.findIndex(day => day.date.getTime() === authorDate.getTime() ); if (dayIndex >= 0) { weekDays[dayIndex].commits++; } } }); } // Smart detection: if week is blank but we have commits, try commit dates // BUT: disable for single-day queries to maintain precision const totalWeekCommits = weekDays.reduce((sum, day) => sum + day.commits, 0); const isSingleDayQuery = timeConfig && timeConfig.type && timeConfig.type.startsWith('last-') && timeConfig.startDate && timeConfig.endDate && timeConfig.startDate.getTime() === timeConfig.endDate.getTime(); if (totalWeekCommits === 0 && repo.commitCount > 0 && repo.commits && Array.isArray(repo.commits) && !isSingleDayQuery) { usedCommitDate = true; // Reset and try with commit dates (approximated from "time ago") weekDays.forEach(day => day.commits = 0); repo.commits.forEach(commit => { if (commit.timeAgo) { // Parse commit date based on current time (this is an approximation) const commitDate = this.parseCommitDate(commit.timeAgo); if (commitDate) { commitDate.setHours(0, 0, 0, 0); // Normalize to start of day const dayIndex = weekDays.findIndex(day => day.date.getTime() === commitDate.getTime() ); if (dayIndex >= 0) { weekDays[dayIndex].commits++; } } } }); } // Generate pattern based on display mode const pattern = weekDays.map(day => { if (day.commits === 0) return '·'; if (showNumbers) { if (day.commits >= 10) return '+'; return day.commits.toString(); } else { if (day.commits >= 3) return 'x'; return '/'; } }); return { pattern: pattern.join(''), usedCommitDate }; } // Helper to parse commit date from "time ago" strings (for commit date fallback) parseCommitDate(timeAgo) { const now = new Date(); // Parse patterns like "26 minutes ago", "22 hours ago", "2 days ago" const match = timeAgo.match(/^(\d+)\s+(minute|hour|day|week|month|year)s?\s+ago$/); if (!match) { return now; // Fallback to now if we can't parse } const value = parseInt(match[1]); const unit = match[2]; switch (unit) { case 'minute': return new Date(now.getTime() - value * 60 * 1000); case 'hour': return new Date(now.getTime() - value * 60 * 60 * 1000); case 'day': return new Date(now.getTime() - value * 24 * 60 * 60 * 1000); case 'week': return new Date(now.getTime() - value * 7 * 24 * 60 * 60 * 1000); case 'month': return new Date(now.getTime() - value * 30 * 24 * 60 * 60 * 1000); case 'year': return new Date(now.getTime() - value * 365 * 24 * 60 * 60 * 1000); default: return now; } } } module.exports = Timeline;