Reading history data.
Time range to analyze.
Velocity metrics object (perDay, perWeek, perMonth, totalChapters, activeDays).
export function computeReadingVelocity(
history: ReadingHistory,
timeRange: TimeRange,
): {
perDay: number;
perWeek: number;
perMonth: number;
totalChapters: number;
activeDays: number;
} {
const filtered = filterHistoryByTimeRange(history, timeRange);
if (!filtered.length) {
return {
perDay: 0,
perWeek: 0,
perMonth: 0,
totalChapters: 0,
activeDays: 0,
};
}
// Establish baseline from pre-range history
const now = Date.now();
const ranges = {
"7d": 7 * 24 * 60 * 60 * 1000,
"30d": 30 * 24 * 60 * 60 * 1000,
"90d": 90 * 24 * 60 * 60 * 1000,
};
const cutoff = timeRange === "all" ? 0 : now - ranges[timeRange];
const preRangeBaseline =
timeRange === "all" ? new Map() : getPreRangeBaseline(history, cutoff);
// Calculate total chapters read (sum of deltas)
const sorted = [...filtered].sort((a, b) => a.timestamp - b.timestamp);
const previousChapters = new Map<string | number, number>(preRangeBaseline);
let totalChapters = 0;
const activeDates = new Set<string>();
for (const entry of sorted) {
const prev = previousChapters.get(entry.mangaId) ?? 0;
const delta = Math.max(0, entry.chaptersRead - prev);
if (delta > 0) {
totalChapters += delta;
const date = getDateKey(entry.timestamp);
activeDates.add(date);
}
previousChapters.set(entry.mangaId, entry.chaptersRead);
}
const activeDays = activeDates.size;
const perDay = activeDays > 0 ? totalChapters / activeDays : 0;
const perWeek = perDay * 7;
const perMonth = perDay * 30;
return {
perDay: Math.round(perDay * 10) / 10,
perWeek: Math.round(perWeek * 10) / 10,
perMonth: Math.round(perMonth * 10) / 10,
totalChapters,
activeDays,
};
}
Computes reading velocity metrics (average chapters per day/week/month) with per-manga baseline.