Feat/add future projection to forgetting curve with today marker (#3732)

* Feat/Add future projection to forgetting curve with today marker

* format

* remove the vertical line
This commit is contained in:
Jarrett Ye 2025-01-18 12:54:53 +08:00 committed by GitHub
parent 899cb89990
commit ac2b44859e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -90,7 +90,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
return; return;
} }
const totalDaysElapsed = (reviewTime - lastReviewTime) / (24 * 60 * 60); const totalDaysElapsed = (reviewTime - lastReviewTime) / 86400;
let elapsedDays = 0; let elapsedDays = 0;
while (elapsedDays < totalDaysElapsed - step) { while (elapsedDays < totalDaysElapsed - step) {
elapsedDays += step; elapsedDays += step;
@ -121,7 +121,7 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
} }
const now = Date.now() / 1000; const now = Date.now() / 1000;
const totalDaysSinceLastReview = (now - lastReviewTime) / (24 * 60 * 60); const totalDaysSinceLastReview = (now - lastReviewTime) / 86400;
let elapsedDays = 0; let elapsedDays = 0;
while (elapsedDays < totalDaysSinceLastReview - step) { while (elapsedDays < totalDaysSinceLastReview - step) {
elapsedDays += step; elapsedDays += step;
@ -144,6 +144,20 @@ export function prepareData(revlog: RevlogEntry[], maxDays: number) {
stability: lastStability, stability: lastStability,
}); });
const previewDays = maxDays - totalDaysSinceLastReview;
let previewDaysElapsed = 0;
while (previewDaysElapsed < previewDays) {
previewDaysElapsed += step;
const retrievability = forgettingCurve(lastStability, elapsedDays + previewDaysElapsed);
data.push({
date: new Date((now + previewDaysElapsed * 86400) * 1000),
daysSinceFirstLearn: data[data.length - 1].daysSinceFirstLearn + step,
elapsedDaysSinceLastReview: totalDaysSinceLastReview + previewDaysElapsed,
retrievability: retrievability * 100,
stability: lastStability,
});
}
const filteredData = filterDataByTimeRange(data, maxDays); const filteredData = filterDataByTimeRange(data, maxDays);
return filteredData; return filteredData;
} }
@ -152,9 +166,14 @@ export function calculateMaxDays(filteredRevlog: RevlogEntry[], timeRange: TimeR
if (filteredRevlog.length === 0) { if (filteredRevlog.length === 0) {
return 0; return 0;
} }
const daysSinceFirstLearn = (Date.now() / 1000 - Number(filteredRevlog[filteredRevlog.length - 1].time)) const today = new Date();
/ (24 * 60 * 60); const daysSinceFirstLearn = (today.getTime() / 1000 - Number(filteredRevlog[filteredRevlog.length - 1].time))
return Math.min(daysSinceFirstLearn, MAX_DAYS[timeRange]); / 86400;
const totalDaysSinceLastReview = (today.getTime() / 1000 - Number(filteredRevlog[0].time))
/ 86400;
const lastScheduledDays = filteredRevlog[0].interval / 86400;
const previewDays = Math.max(lastScheduledDays * 1.5 - totalDaysSinceLastReview, lastScheduledDays * 0.5);
return Math.min(daysSinceFirstLearn + previewDays, MAX_DAYS[timeRange]);
} }
export function renderForgettingCurve( export function renderForgettingCurve(
@ -181,7 +200,7 @@ export function renderForgettingCurve(
setDataAvailable(svg, true); setDataAvailable(svg, true);
} }
svg.select(".forgetting-curve-line").remove(); svg.selectAll(".forgetting-curve-line").remove();
svg.select(".hover-columns").remove(); svg.select(".hover-columns").remove();
const xMin = min(data, d => d.date); const xMin = min(data, d => d.date);
@ -242,14 +261,30 @@ export function renderForgettingCurve(
.attr("offset", d => d.offset) .attr("offset", d => d.offset)
.attr("stop-color", d => d.color); .attr("stop-color", d => d.color);
// Split data into past and future
const today = new Date();
const pastData = data.filter(d => d.date <= today);
const futureData = data.filter(d => d.date >= today);
// Draw solid line for past data
svg.append("path") svg.append("path")
.datum(data) .datum(pastData)
.attr("class", "forgetting-curve-line") .attr("class", "forgetting-curve-line")
.attr("fill", "none") .attr("fill", "none")
.attr("stroke", "url(#line-gradient)") .attr("stroke", "url(#line-gradient)")
.attr("stroke-width", 1.5) .attr("stroke-width", 1.5)
.attr("d", lineGenerator); .attr("d", lineGenerator);
// Draw dashed line for future data
svg.append("path")
.datum(futureData)
.attr("class", "forgetting-curve-line")
.attr("fill", "none")
.attr("stroke", "url(#line-gradient)")
.attr("stroke-width", 1.5)
.attr("stroke-dasharray", "4 4")
.attr("d", lineGenerator);
svg.select(".desired-retention-line").remove(); svg.select(".desired-retention-line").remove();
if (desiredRetentionY > yMin) { if (desiredRetentionY > yMin) {
svg.append("line") svg.append("line")