Replace CountMethodRadios with SeparateInactiveCheckbox method
* cards will still be mostly counted by ctype rather than queue * if the user wants to include inactive cards buried and suspended will be first filtered out, before the rest is counted by ctype
This commit is contained in:
parent
2ae09ae39e
commit
681d82f5cc
@ -1,15 +1,15 @@
|
|||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { CardCountMethod, defaultGraphBounds } from "./graph-helpers";
|
import { defaultGraphBounds } from "./graph-helpers";
|
||||||
import { gatherData, renderCards } from "./card-counts";
|
import { gatherData, renderCards } from "./card-counts";
|
||||||
import type { GraphData, TableDatum } from "./card-counts";
|
import type { GraphData, TableDatum } from "./card-counts";
|
||||||
import type pb from "anki/backend_proto";
|
import type pb from "anki/backend_proto";
|
||||||
import type { I18n } from "anki/i18n";
|
import type { I18n } from "anki/i18n";
|
||||||
import CountMethodRadios from "./CountMethodRadios.svelte";
|
import SeparateInactiveCheckbox from "./SeparateInactiveCheckbox.svelte";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut;
|
export let sourceData: pb.BackendProto.GraphsOut;
|
||||||
export let i18n: I18n;
|
export let i18n: I18n;
|
||||||
|
|
||||||
let cardCountMethod = CardCountMethod.ByType;
|
let separateInactive = false;
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
|
|
||||||
let bounds = defaultGraphBounds();
|
let bounds = defaultGraphBounds();
|
||||||
@ -20,7 +20,7 @@
|
|||||||
let tableData = (null as unknown) as TableDatum[];
|
let tableData = (null as unknown) as TableDatum[];
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
graphData = gatherData(sourceData, cardCountMethod, i18n);
|
graphData = gatherData(sourceData, separateInactive, i18n);
|
||||||
tableData = renderCards(svg as any, bounds, graphData);
|
tableData = renderCards(svg as any, bounds, graphData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<h1>{graphData.title}</h1>
|
<h1>{graphData.title}</h1>
|
||||||
|
|
||||||
<div class="range-box-inner">
|
<div class="range-box-inner">
|
||||||
<CountMethodRadios bind:cardCountMethod {i18n} />
|
<SeparateInactiveCheckbox bind:separateInactive {i18n} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="counts-outer">
|
<div class="counts-outer">
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
<script lang="typescript">
|
|
||||||
import type { I18n } from "anki/i18n";
|
|
||||||
import { CardCountMethod } from "./graph-helpers";
|
|
||||||
|
|
||||||
export let i18n: I18n;
|
|
||||||
export let cardCountMethod: CardCountMethod;
|
|
||||||
|
|
||||||
const byType = "By card type";
|
|
||||||
const byQueue = "By scheduling queue";
|
|
||||||
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label>
|
|
||||||
<input type="radio" bind:group={cardCountMethod} value={CardCountMethod.ByType} />
|
|
||||||
{byType}
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="radio" bind:group={cardCountMethod} value={CardCountMethod.ByQueue} />
|
|
||||||
{byQueue}
|
|
||||||
</label>
|
|
14
ts/graphs/SeparateInactiveCheckbox.svelte
Normal file
14
ts/graphs/SeparateInactiveCheckbox.svelte
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import type { I18n } from "anki/i18n";
|
||||||
|
|
||||||
|
export let i18n: I18n;
|
||||||
|
export let separateInactive: bool = false;
|
||||||
|
|
||||||
|
const label = "Separate suspended/buried cards";
|
||||||
|
const all = i18n.tr(i18n.TR.STATISTICS_RANGE_ALL_TIME);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" bind:checked={separateInactive} />
|
||||||
|
{label}
|
||||||
|
</label>
|
@ -15,81 +15,48 @@ import { scaleLinear } from "d3-scale";
|
|||||||
import { pie, arc } from "d3-shape";
|
import { pie, arc } from "d3-shape";
|
||||||
import { interpolate } from "d3-interpolate";
|
import { interpolate } from "d3-interpolate";
|
||||||
import type { GraphBounds } from "./graph-helpers";
|
import type { GraphBounds } from "./graph-helpers";
|
||||||
import { CardCountMethod } from "./graph-helpers";
|
|
||||||
import { cumsum } from "d3-array";
|
import { cumsum } from "d3-array";
|
||||||
import type { I18n } from "anki/i18n";
|
import type { I18n } from "anki/i18n";
|
||||||
|
|
||||||
type Count = [string, number, string];
|
type Count = [string, number];
|
||||||
export interface GraphData {
|
export interface GraphData {
|
||||||
title: string;
|
title: string;
|
||||||
counts: Count[];
|
counts: Count[];
|
||||||
totalCards: number;
|
totalCards: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const barColours = {
|
const barColours = [
|
||||||
new: schemeBlues[5][2],
|
schemeBlues[5][2] /* new */,
|
||||||
review: schemeGreens[5][2],
|
schemeOranges[5][2] /* learn */,
|
||||||
young: schemeGreens[5][2],
|
schemeOranges[5][3] /* relearn */,
|
||||||
mature: schemeGreens[5][3],
|
schemeGreens[5][2] /* young */,
|
||||||
learn: schemeOranges[5][2],
|
schemeGreens[5][3] /* mature */,
|
||||||
relearn: schemeOranges[5][3],
|
"#FFDC41" /* suspended */,
|
||||||
suspended: "#FFDC41",
|
"grey" /* buried */,
|
||||||
buried: "grey",
|
];
|
||||||
};
|
|
||||||
|
|
||||||
function gatherByQueue(cards: pb.BackendProto.ICard[], i18n: I18n): Count[] {
|
function countCards(cards: pb.BackendProto.ICard[], separateInactive: boolean, i18n: I18n): Count[] {
|
||||||
let newCards = 0;
|
let newCards = 0;
|
||||||
let learn = 0;
|
let learn = 0;
|
||||||
let review = 0;
|
let relearn = 0;
|
||||||
|
let young = 0;
|
||||||
|
let mature = 0;
|
||||||
let suspended = 0;
|
let suspended = 0;
|
||||||
let buried = 0;
|
let buried = 0;
|
||||||
|
|
||||||
for (const card of cards as pb.BackendProto.Card[]) {
|
for (const card of cards as pb.BackendProto.Card[]) {
|
||||||
|
if (separateInactive) {
|
||||||
switch (card.queue) {
|
switch (card.queue) {
|
||||||
case CardQueue.New:
|
|
||||||
newCards += 1;
|
|
||||||
break;
|
|
||||||
case CardQueue.Review:
|
|
||||||
review += 1;
|
|
||||||
break;
|
|
||||||
case CardQueue.Learn:
|
|
||||||
case CardQueue.DayLearn:
|
|
||||||
case CardQueue.PreviewRepeat:
|
|
||||||
learn += 1;
|
|
||||||
break;
|
|
||||||
case CardQueue.Suspended:
|
case CardQueue.Suspended:
|
||||||
suspended += 1;
|
suspended += 1;
|
||||||
break;
|
continue;
|
||||||
case CardQueue.SchedBuried:
|
case CardQueue.SchedBuried:
|
||||||
case CardQueue.UserBuried:
|
case CardQueue.UserBuried:
|
||||||
buried += 1;
|
buried += 1;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const counts: Count[] = [
|
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_NEW_CARDS), newCards, barColours.new],
|
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_LEARNING_CARDS), learn, barColours.learn],
|
|
||||||
["Review", review, barColours.review],
|
|
||||||
[
|
|
||||||
i18n.tr(i18n.TR.STATISTICS_COUNTS_SUSPENDED_CARDS),
|
|
||||||
suspended,
|
|
||||||
barColours.suspended,
|
|
||||||
],
|
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_BURIED_CARDS), buried, barColours.buried],
|
|
||||||
];
|
|
||||||
|
|
||||||
return counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function gatherByCtype(cards: pb.BackendProto.ICard[], i18n: I18n): Count[] {
|
|
||||||
let newCards = 0;
|
|
||||||
let learn = 0;
|
|
||||||
let young = 0;
|
|
||||||
let mature = 0;
|
|
||||||
let relearn = 0;
|
|
||||||
|
|
||||||
for (const card of cards as pb.BackendProto.Card[]) {
|
|
||||||
switch (card.ctype) {
|
switch (card.ctype) {
|
||||||
case CardType.New:
|
case CardType.New:
|
||||||
newCards += 1;
|
newCards += 1;
|
||||||
@ -110,31 +77,28 @@ function gatherByCtype(cards: pb.BackendProto.ICard[], i18n: I18n): Count[] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const counts: Count[] = [
|
const counts: Count[] = [
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_NEW_CARDS), newCards, barColours.new],
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_NEW_CARDS), newCards],
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_LEARNING_CARDS), learn, barColours.learn],
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_LEARNING_CARDS), learn],
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS), young, barColours.young],
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_RELEARNING_CARDS), relearn],
|
||||||
[i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS), mature, barColours.mature],
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS), young],
|
||||||
[
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS), mature],
|
||||||
i18n.tr(i18n.TR.STATISTICS_COUNTS_RELEARNING_CARDS),
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_SUSPENDED_CARDS), suspended],
|
||||||
relearn,
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_BURIED_CARDS), buried],
|
||||||
barColours.relearn,
|
]
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(
|
export function gatherData(
|
||||||
data: pb.BackendProto.GraphsOut,
|
data: pb.BackendProto.GraphsOut,
|
||||||
method: CardCountMethod,
|
separateInactive: boolean,
|
||||||
i18n: I18n
|
i18n: I18n
|
||||||
): GraphData {
|
): GraphData {
|
||||||
const totalCards = data.cards.length;
|
const totalCards = data.cards.length;
|
||||||
const counts =
|
const counts = countCards(data.cards, separateInactive, i18n)
|
||||||
method === CardCountMethod.ByType
|
|
||||||
? gatherByCtype(data.cards, i18n)
|
|
||||||
: gatherByQueue(data.cards, i18n);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: i18n.tr(i18n.TR.STATISTICS_COUNTS_TITLE),
|
title: i18n.tr(i18n.TR.STATISTICS_COUNTS_TITLE),
|
||||||
@ -155,7 +119,6 @@ export interface SummedDatum {
|
|||||||
label: string;
|
label: string;
|
||||||
// count of this particular item
|
// count of this particular item
|
||||||
count: number;
|
count: number;
|
||||||
colour: string;
|
|
||||||
// running total
|
// running total
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
@ -178,7 +141,6 @@ export function renderCards(
|
|||||||
return {
|
return {
|
||||||
label: count[0],
|
label: count[0],
|
||||||
count: count[1],
|
count: count[1],
|
||||||
colour: count[2],
|
|
||||||
idx,
|
idx,
|
||||||
total: n,
|
total: n,
|
||||||
} as SummedDatum;
|
} as SummedDatum;
|
||||||
@ -199,20 +161,16 @@ export function renderCards(
|
|||||||
.selectAll("path")
|
.selectAll("path")
|
||||||
.data(pieData)
|
.data(pieData)
|
||||||
.join(
|
.join(
|
||||||
(enter) =>
|
(enter) => enter
|
||||||
enter
|
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("fill", (_d, i) => {
|
.attr("fill", (_d, idx) => {
|
||||||
return data[i].colour;
|
return barColours[idx];
|
||||||
})
|
})
|
||||||
.attr("d", arcGen as any),
|
.attr("d", arcGen as any),
|
||||||
function (update) {
|
function (update) {
|
||||||
return update.call((d) =>
|
return update.call((d) =>
|
||||||
d
|
d
|
||||||
.transition(trans)
|
.transition(trans)
|
||||||
.attr("fill", (_d, i) => {
|
|
||||||
return data[i].colour;
|
|
||||||
})
|
|
||||||
.attrTween("d", (d) => {
|
.attrTween("d", (d) => {
|
||||||
const interpolator = interpolate(
|
const interpolator = interpolate(
|
||||||
{ startAngle: 0, endAngle: 0 },
|
{ startAngle: 0, endAngle: 0 },
|
||||||
@ -227,13 +185,13 @@ export function renderCards(
|
|||||||
|
|
||||||
x.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
|
x.range([bounds.marginLeft, bounds.width - bounds.marginRight]);
|
||||||
|
|
||||||
const tableData = data.map((d) => {
|
const tableData = data.map((d, idx) => {
|
||||||
const percent = ((d.count / xMax) * 100).toFixed(1);
|
const percent = ((d.count / xMax) * 100).toFixed(1);
|
||||||
return {
|
return {
|
||||||
label: d.label,
|
label: d.label,
|
||||||
count: d.count,
|
count: d.count,
|
||||||
percent: `${percent}%`,
|
percent: `${percent}%`,
|
||||||
colour: d.colour,
|
colour: barColours[idx],
|
||||||
} as TableDatum;
|
} as TableDatum;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -33,12 +33,6 @@ export enum GraphRange {
|
|||||||
AllTime = 3,
|
AllTime = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
// how card should be counted
|
|
||||||
export enum CardCountMethod {
|
|
||||||
ByType = 0,
|
|
||||||
ByQueue = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GraphsContext {
|
export interface GraphsContext {
|
||||||
cards: pb.BackendProto.Card[];
|
cards: pb.BackendProto.Card[];
|
||||||
revlog: pb.BackendProto.RevlogEntry[];
|
revlog: pb.BackendProto.RevlogEntry[];
|
||||||
|
Loading…
Reference in New Issue
Block a user