mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Feat/add a toggle in the simulator to display time or review count (#3523)
Co-authored-by: Damien Elmes <dae@users.noreply.github.com>
This commit is contained in:
parent
1aa734ad28
commit
eacd5bf908
2 changed files with 48 additions and 15 deletions
|
@ -40,6 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import NoDataOverlay from "../graphs/NoDataOverlay.svelte";
|
||||
import TableData from "../graphs/TableData.svelte";
|
||||
import { defaultGraphBounds, type TableDatum } from "../graphs/graph-helpers";
|
||||
import InputBox from "../graphs/InputBox.svelte";
|
||||
|
||||
export let state: DeckOptionsState;
|
||||
export let openHelpModal: (String) => void;
|
||||
|
@ -73,6 +74,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
| ComputeRetentionProgress
|
||||
| undefined;
|
||||
|
||||
let showTime = false;
|
||||
|
||||
const optimalRetentionRequest = new ComputeOptimalRetentionRequest({
|
||||
daysToSimulate: 365,
|
||||
lossAversion: 2.5,
|
||||
|
@ -305,6 +308,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
return result;
|
||||
}
|
||||
|
||||
function addArrays(arr1: number[], arr2: number[]): number[] {
|
||||
return arr1.map((value, index) => value + arr2[index]);
|
||||
}
|
||||
|
||||
$: simulateProgressString = "";
|
||||
|
||||
async function simulateFsrs(): Promise<void> {
|
||||
|
@ -328,23 +335,37 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
resp.dailyTimeCost,
|
||||
Math.ceil(simulateFsrsRequest.daysToSimulate / 50),
|
||||
);
|
||||
const dailyReviewCount = movingAverage(
|
||||
addArrays(resp.dailyReviewCount, resp.dailyNewCount),
|
||||
Math.ceil(simulateFsrsRequest.daysToSimulate / 50),
|
||||
);
|
||||
points = points.concat(
|
||||
dailyTimeCost.map((v, i) => ({
|
||||
x: i,
|
||||
y: v,
|
||||
timeCost: v,
|
||||
count: dailyReviewCount[i],
|
||||
label: simulationNumber,
|
||||
})),
|
||||
);
|
||||
tableData = renderSimulationChart(svg as SVGElement, bounds, points);
|
||||
tableData = renderSimulationChart(
|
||||
svg as SVGElement,
|
||||
bounds,
|
||||
points,
|
||||
showTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: tableData = renderSimulationChart(svg as SVGElement, bounds, points, showTime);
|
||||
|
||||
function clearSimulation(): void {
|
||||
points = points.filter((p) => p.label !== simulationNumber);
|
||||
simulationNumber = Math.max(0, simulationNumber - 1);
|
||||
tableData = renderSimulationChart(svg as SVGElement, bounds, points);
|
||||
tableData = renderSimulationChart(svg as SVGElement, bounds, points, showTime);
|
||||
}
|
||||
|
||||
const label = tr.statisticsReviewsTimeCheckbox();
|
||||
</script>
|
||||
|
||||
<SpinBoxFloatRow
|
||||
|
@ -472,7 +493,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
max={3650}
|
||||
>
|
||||
<SettingTitle on:click={() => openHelpModal("simulateFsrsReview")}>
|
||||
Days to simulate
|
||||
{tr.deckConfigDaysToSimulate()}
|
||||
</SettingTitle>
|
||||
</SpinBoxRow>
|
||||
|
||||
|
@ -491,10 +512,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
bind:value={simulateFsrsRequest.newLimit}
|
||||
defaultValue={defaults.newPerDay}
|
||||
min={0}
|
||||
max={1000}
|
||||
max={9999}
|
||||
>
|
||||
<SettingTitle on:click={() => openHelpModal("simulateFsrsReview")}>
|
||||
New cards/day
|
||||
{tr.schedulingNewCardsday()}
|
||||
</SettingTitle>
|
||||
</SpinBoxRow>
|
||||
|
||||
|
@ -502,10 +523,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
bind:value={simulateFsrsRequest.reviewLimit}
|
||||
defaultValue={defaults.reviewsPerDay}
|
||||
min={0}
|
||||
max={1000}
|
||||
max={9999}
|
||||
>
|
||||
<SettingTitle on:click={() => openHelpModal("simulateFsrsReview")}>
|
||||
Maximum reviews/day
|
||||
{tr.schedulingMaximumReviewsday()}
|
||||
</SettingTitle>
|
||||
</SpinBoxRow>
|
||||
|
||||
|
@ -516,7 +537,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
max={36500}
|
||||
>
|
||||
<SettingTitle on:click={() => openHelpModal("simulateFsrsReview")}>
|
||||
Maximum interval
|
||||
{tr.schedulingMaximumInterval()}
|
||||
</SettingTitle>
|
||||
</SpinBoxRow>
|
||||
|
||||
|
@ -538,6 +559,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<div>{simulateProgressString}</div>
|
||||
|
||||
<Graph {title}>
|
||||
<InputBox>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={showTime} />
|
||||
{label}
|
||||
</label>
|
||||
</InputBox>
|
||||
|
||||
<svg bind:this={svg} viewBox={`0 0 ${bounds.width} ${bounds.height}`}>
|
||||
<CumulativeOverlay />
|
||||
<HoverColumns />
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
select,
|
||||
} from "d3";
|
||||
|
||||
import * as tr from "@generated/ftl";
|
||||
import { timeSpan } from "@tslib/time";
|
||||
import type { GraphBounds, TableDatum } from "./graph-helpers";
|
||||
import { setDataAvailable } from "./graph-helpers";
|
||||
|
@ -23,7 +24,8 @@ import { hideTooltip, showTooltip } from "./tooltip-utils.svelte";
|
|||
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
timeCost: number;
|
||||
count: number;
|
||||
label: number;
|
||||
}
|
||||
|
||||
|
@ -31,6 +33,7 @@ export function renderSimulationChart(
|
|||
svgElem: SVGElement,
|
||||
bounds: GraphBounds,
|
||||
data: Point[],
|
||||
showTime: boolean,
|
||||
): TableDatum[] {
|
||||
const svg = select(svgElem);
|
||||
svg.selectAll(".lines").remove();
|
||||
|
@ -62,10 +65,10 @@ export function renderSimulationChart(
|
|||
// y scale
|
||||
|
||||
const yTickFormat = (n: number): string => {
|
||||
return timeSpan(n, true);
|
||||
return showTime ? timeSpan(n, true) : n.toString();
|
||||
};
|
||||
|
||||
const yMax = max(convertedData, d => d.y)!;
|
||||
const yMax = showTime ? max(convertedData, d => d.timeCost)! : max(convertedData, d => d.count)!;
|
||||
const y = scaleLinear()
|
||||
.range([bounds.height - bounds.marginBottom, bounds.marginTop])
|
||||
.domain([0, yMax])
|
||||
|
@ -91,10 +94,10 @@ export function renderSimulationChart(
|
|||
.attr("dy", "1em")
|
||||
.attr("fill", "currentColor")
|
||||
.style("text-anchor", "middle")
|
||||
.text("Review Time per day");
|
||||
.text(showTime ? "Review Time per day" : "Review Count per day");
|
||||
|
||||
// x lines
|
||||
const points = convertedData.map((d) => [x(d.date), y(d.y), d.label]);
|
||||
const points = convertedData.map((d) => [x(d.date), y(showTime ? d.timeCost : d.count), d.label]);
|
||||
const groups = rollup(points, v => Object.assign(v, { z: v[0][2] }), d => d[2]);
|
||||
|
||||
const color = schemeCategory10;
|
||||
|
@ -161,7 +164,9 @@ export function renderSimulationChart(
|
|||
const days = +((date.getTime() - Date.now()) / (60 * 60 * 24 * 1000)).toFixed();
|
||||
let tooltipContent = `Date: ${localizedDate(date)}<br>In ${days} Days<br>`;
|
||||
for (const [key, value] of Object.entries(groupData)) {
|
||||
tooltipContent += `#${key}: ${timeSpan(value)}<br>`;
|
||||
tooltipContent += `#${key}: ${
|
||||
showTime ? timeSpan(value) : tr.statisticsReviews({ reviews: Math.round(value) })
|
||||
}<br>`;
|
||||
}
|
||||
|
||||
showTooltip(tooltipContent, event.pageX, event.pageY);
|
||||
|
|
Loading…
Reference in a new issue