UNPKG

@ankimcp/anki-mcp-server

Version:

Model Context Protocol server for Anki - enables AI assistants to interact with your Anki flashcards

126 lines • 4.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeDistribution = computeDistribution; exports.computeRetention = computeRetention; exports.calculateStreak = calculateStreak; const date_utils_1 = require("./date.utils.js"); function formatNumber(n) { if (Number.isInteger(n)) { return n.toString(); } return n.toFixed(1); } function createEmptyBuckets(config) { const buckets = {}; const { boundaries, formatLabel, unitSuffix } = config; if (boundaries.length === 0) { return buckets; } const labels = [ formatLabel ? formatLabel(null, boundaries[0]) : `<${formatNumber(boundaries[0])}${unitSuffix || ""}`, ]; for (let i = 0; i < boundaries.length - 1; i++) { labels.push(formatLabel ? formatLabel(boundaries[i], boundaries[i + 1]) : `${formatNumber(boundaries[i])}-${formatNumber(boundaries[i + 1])}${unitSuffix || ""}`); } labels.push(formatLabel ? formatLabel(boundaries[boundaries.length - 1], null) : `>${formatNumber(boundaries[boundaries.length - 1])}${unitSuffix || ""}`); labels.forEach((label) => { buckets[label] = 0; }); return buckets; } function computeBuckets(sortedValues, config) { const buckets = createEmptyBuckets(config); const { boundaries, formatLabel, unitSuffix } = config; if (boundaries.length === 0) { return buckets; } for (const value of sortedValues) { let placed = false; if (value < boundaries[0]) { const label = formatLabel ? formatLabel(null, boundaries[0]) : `<${formatNumber(boundaries[0])}${unitSuffix || ""}`; buckets[label]++; continue; } for (let i = 0; i < boundaries.length - 1; i++) { if (value >= boundaries[i] && value < boundaries[i + 1]) { const label = formatLabel ? formatLabel(boundaries[i], boundaries[i + 1]) : `${formatNumber(boundaries[i])}-${formatNumber(boundaries[i + 1])}${unitSuffix || ""}`; buckets[label]++; placed = true; break; } } if (!placed) { const label = formatLabel ? formatLabel(boundaries[boundaries.length - 1], null) : `>${formatNumber(boundaries[boundaries.length - 1])}${unitSuffix || ""}`; buckets[label]++; } } return buckets; } function computeDistribution(values, config) { if (values.length === 0) { return { mean: 0, median: 0, min: 0, max: 0, count: 0, buckets: createEmptyBuckets(config), }; } const sorted = [...values].sort((a, b) => a - b); const sum = sorted.reduce((acc, v) => acc + v, 0); const medianIndex = Math.floor(sorted.length / 2); const median = sorted.length % 2 === 0 ? (sorted[medianIndex - 1] + sorted[medianIndex]) / 2 : sorted[medianIndex]; return { mean: sum / sorted.length, median, min: sorted[0], max: sorted[sorted.length - 1], count: sorted.length, buckets: computeBuckets(sorted, config), }; } function computeRetention(buttonPresses) { const counts = { again: 0, hard: 0, good: 0, easy: 0 }; buttonPresses.forEach((button) => { if (button === 1) counts.again++; else if (button === 2) counts.hard++; else if (button === 3) counts.good++; else if (button === 4) counts.easy++; }); const total = counts.again + counts.hard + counts.good + counts.easy; const remembered = counts.hard + counts.good + counts.easy; return { overall: total > 0 ? remembered / total : 0, by_rating: counts, }; } function calculateStreak(reviewsByDay, todayKey) { const studiedDays = new Set(reviewsByDay.filter((day) => day.count > 0).map((day) => day.date)); let streak = 0; let dayKey = todayKey; while (studiedDays.has(dayKey)) { streak++; dayKey = (0, date_utils_1.addDaysToDayKey)(dayKey, -1); } return streak; } //# sourceMappingURL=stats.utils.js.map