mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
i18n card counts
This commit is contained in:
parent
01f3a71f0a
commit
28a6755c75
13 changed files with 63 additions and 75 deletions
|
@ -989,52 +989,14 @@ message GraphsIn {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GraphsOut {
|
message GraphsOut {
|
||||||
// CardsGraphData cards = 1;
|
|
||||||
// TodayGraphData today = 3;
|
|
||||||
// ButtonsGraphData buttons = 4;
|
|
||||||
// repeated HourGraphData hours = 2;
|
|
||||||
repeated Card cards = 1;
|
repeated Card cards = 1;
|
||||||
repeated RevlogEntry revlog = 2;
|
repeated RevlogEntry revlog = 2;
|
||||||
|
|
||||||
uint32 days_elapsed = 3;
|
uint32 days_elapsed = 3;
|
||||||
// Based on rollover hour
|
// Based on rollover hour
|
||||||
uint32 next_day_at_secs = 4;
|
uint32 next_day_at_secs = 4;
|
||||||
uint32 scheduler_version = 5;
|
uint32 scheduler_version = 5;
|
||||||
/// Seconds to add to UTC timestamps to get local time.
|
/// Seconds to add to UTC timestamps to get local time.
|
||||||
uint32 local_offset_secs = 7;
|
uint32 local_offset_secs = 7;
|
||||||
|
|
||||||
uint32 note_count = 10;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
message CardsGraphData {
|
|
||||||
uint32 mature_count = 7;
|
|
||||||
uint32 young_or_learning_count = 8;
|
|
||||||
uint32 new_count = 9;
|
|
||||||
uint32 suspended_or_buried_count = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TodayGraphData {
|
|
||||||
uint32 answer_count = 1;
|
|
||||||
uint32 answer_millis = 2;
|
|
||||||
uint32 correct_count = 3;
|
|
||||||
uint32 learn_count = 4;
|
|
||||||
uint32 review_count = 5;
|
|
||||||
uint32 relearn_count = 6;
|
|
||||||
uint32 early_review_count = 7;
|
|
||||||
uint32 mature_count = 8;
|
|
||||||
uint32 mature_correct = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
message HourGraphData {
|
|
||||||
uint32 review_count = 1;
|
|
||||||
uint32 correct_count = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ButtonsGraphData {
|
|
||||||
repeated uint32 learn = 1;
|
|
||||||
repeated uint32 young = 2;
|
|
||||||
repeated uint32 mature = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message RevlogEntry {
|
message RevlogEntry {
|
||||||
|
|
|
@ -77,3 +77,10 @@ statistics-today-type-counts = Learn: { $learnCount }, Review: { $reviewCount },
|
||||||
statistics-today-no-cards = No cards have been studied today.
|
statistics-today-no-cards = No cards have been studied today.
|
||||||
statistics-today-no-mature-cards = No mature cards were studied today.
|
statistics-today-no-mature-cards = No mature cards were studied today.
|
||||||
statistics-today-correct-mature = Correct answers on mature cards: { $correct }/{ $total } ({ $percent }%)
|
statistics-today-correct-mature = Correct answers on mature cards: { $correct }/{ $total } ({ $percent }%)
|
||||||
|
statistics-counts-total-cards = Total
|
||||||
|
statistics-counts-new-cards = New
|
||||||
|
statistics-counts-young-cards = Young
|
||||||
|
statistics-counts-mature-cards = Mature
|
||||||
|
statistics-counts-suspended-cards = Suspended
|
||||||
|
statistics-counts-buried-cards = Buried
|
||||||
|
statistics-counts-title = Card Counts
|
||||||
|
|
|
@ -42,7 +42,6 @@ impl Collection {
|
||||||
next_day_at_secs: timing.next_day_at as u32,
|
next_day_at_secs: timing.next_day_at as u32,
|
||||||
scheduler_version: self.sched_ver() as u32,
|
scheduler_version: self.sched_ver() as u32,
|
||||||
local_offset_secs: local_offset_secs as u32,
|
local_offset_secs: local_offset_secs as u32,
|
||||||
note_count: 0,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,18 @@
|
||||||
import pb from "./backend/proto";
|
import pb from "./backend/proto";
|
||||||
import { FluentBundle, FluentResource, FluentNumber } from "@fluent/bundle";
|
import { FluentBundle, FluentResource, FluentNumber } from "@fluent/bundle";
|
||||||
|
|
||||||
function formatNumbers(args?: Record<string, any>): void {
|
type RecordVal = number | string | FluentNumber;
|
||||||
|
|
||||||
|
function formatNumbers(args?: Record<string, RecordVal>): void {
|
||||||
if (!args) {
|
if (!args) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(args)) {
|
for (const key of Object.keys(args)) {
|
||||||
if (typeof args[key] === "number") {
|
if (typeof args[key] === "number") {
|
||||||
args[key] = new FluentNumber(args[key], { maximumSignificantDigits: 2 });
|
args[key] = new FluentNumber(args[key] as number, {
|
||||||
|
maximumSignificantDigits: 2,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +24,7 @@ export class I18n {
|
||||||
bundles: FluentBundle[] = [];
|
bundles: FluentBundle[] = [];
|
||||||
TR = pb.BackendProto.FluentString;
|
TR = pb.BackendProto.FluentString;
|
||||||
|
|
||||||
tr(id: pb.BackendProto.FluentString, args?: Record<string, any>): string {
|
tr(id: pb.BackendProto.FluentString, args?: Record<string, RecordVal>): string {
|
||||||
formatNumbers(args);
|
formatNumbers(args);
|
||||||
const key = this.keyName(id);
|
const key = this.keyName(id);
|
||||||
for (const bundle of this.bundles) {
|
for (const bundle of this.bundles) {
|
||||||
|
|
|
@ -1,17 +1,38 @@
|
||||||
<script lang="typescript">
|
<script lang="typescript">
|
||||||
import { gatherData, CardCounts } from "./card-counts";
|
import { gatherData, CardCounts } from "./card-counts";
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||||
|
export let i18n: I18n;
|
||||||
|
|
||||||
let cardCounts: CardCounts | null = null;
|
let cardCounts: CardCounts | null = null;
|
||||||
$: if (sourceData) {
|
$: if (sourceData) {
|
||||||
console.log("gathering data");
|
console.log("gathering data");
|
||||||
cardCounts = gatherData(sourceData);
|
cardCounts = gatherData(sourceData, i18n);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="graph">
|
<style>
|
||||||
<h1>Card Counts</h1>
|
.counts-outer {
|
||||||
{JSON.stringify(cardCounts)}
|
display: flex;
|
||||||
</div>
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{#if cardCounts}
|
||||||
|
<div class="graph">
|
||||||
|
<h1>{cardCounts.title}</h1>
|
||||||
|
<div class="counts-outer">
|
||||||
|
{#each cardCounts.counts as count}
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<b>{count[0]}</b>
|
||||||
|
</div>
|
||||||
|
<div>{count[1]}</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
|
@ -125,7 +125,7 @@
|
||||||
<div class="range-box-pad" />
|
<div class="range-box-pad" />
|
||||||
|
|
||||||
<TodayStats {sourceData} {i18n} />
|
<TodayStats {sourceData} {i18n} />
|
||||||
<CardCounts {sourceData} />
|
<CardCounts {sourceData} {i18n} />
|
||||||
<FutureDue {sourceData} />
|
<FutureDue {sourceData} />
|
||||||
<ReviewsGraph {sourceData} {revlogRange} />
|
<ReviewsGraph {sourceData} {revlogRange} />
|
||||||
<IntervalsGraph {sourceData} />
|
<IntervalsGraph {sourceData} />
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
let svg = null as HTMLElement | SVGElement | null;
|
let svg = null as HTMLElement | SVGElement | null;
|
||||||
let range: ReviewRange;
|
let range: ReviewRange;
|
||||||
let showTime = false;
|
let showTime = false;
|
||||||
let tooltip = null as null | HTMLDivElement;
|
|
||||||
|
|
||||||
$: switch (revlogRange as RevlogRange) {
|
$: switch (revlogRange as RevlogRange) {
|
||||||
case RevlogRange.Month:
|
case RevlogRange.Month:
|
||||||
|
@ -38,7 +37,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (graphData) {
|
$: if (graphData) {
|
||||||
renderReviews(svg as SVGElement, bounds, graphData, range, showTime, tooltip);
|
renderReviews(svg as SVGElement, bounds, graphData, range, showTime);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -83,6 +82,4 @@
|
||||||
<AxisLabels {bounds} {xText} {yText} />
|
<AxisLabels {bounds} {xText} {yText} />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div bind:this={tooltip} class="tooltip-area" />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,19 +3,15 @@
|
||||||
|
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { CardQueue } from "../cards";
|
import { CardQueue } from "../cards";
|
||||||
|
import { I18n } from "../i18n";
|
||||||
|
|
||||||
|
type Count = [string, number];
|
||||||
export interface CardCounts {
|
export interface CardCounts {
|
||||||
totalCards: number;
|
title: string;
|
||||||
totalNotes: number;
|
counts: Count[];
|
||||||
newCards: number;
|
|
||||||
young: number;
|
|
||||||
mature: number;
|
|
||||||
suspended: number;
|
|
||||||
buried: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gatherData(data: pb.BackendProto.GraphsOut): CardCounts {
|
export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): CardCounts {
|
||||||
const totalNotes = data.noteCount;
|
|
||||||
const totalCards = data.cards.length;
|
const totalCards = data.cards.length;
|
||||||
let newCards = 0;
|
let newCards = 0;
|
||||||
let young = 0;
|
let young = 0;
|
||||||
|
@ -48,13 +44,17 @@ export function gatherData(data: pb.BackendProto.GraphsOut): CardCounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const counts = [
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_TOTAL_CARDS), totalCards] as Count,
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_NEW_CARDS), newCards] as Count,
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_YOUNG_CARDS), young] as Count,
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_MATURE_CARDS), mature] as Count,
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_SUSPENDED_CARDS), suspended] as Count,
|
||||||
|
[i18n.tr(i18n.TR.STATISTICS_COUNTS_BURIED_CARDS), buried] as Count,
|
||||||
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalCards,
|
title: i18n.tr(i18n.TR.STATISTICS_COUNTS_TITLE),
|
||||||
totalNotes,
|
counts,
|
||||||
newCards,
|
|
||||||
young,
|
|
||||||
mature,
|
|
||||||
suspended,
|
|
||||||
buried,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import pb from "../backend/proto";
|
import pb from "../backend/proto";
|
||||||
import { extent, histogram, rollup, max, sum, Bin } from "d3-array";
|
import { extent, histogram, rollup, sum, Bin } from "d3-array";
|
||||||
import { scaleLinear, scaleSequential } from "d3-scale";
|
import { scaleLinear, scaleSequential } from "d3-scale";
|
||||||
import { CardQueue } from "../cards";
|
import { CardQueue } from "../cards";
|
||||||
import { HistogramData } from "./histogram-graph";
|
import { HistogramData } from "./histogram-graph";
|
||||||
|
@ -65,7 +65,7 @@ export function buildHistogram(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [xMinOrig, origXMax] = extent<number>(data.keys());
|
const [_xMinOrig, origXMax] = extent<number>(data.keys());
|
||||||
const xMin = 0;
|
const xMin = 0;
|
||||||
let xMax = origXMax;
|
let xMax = origXMax;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { setupI18n } from "../i18n";
|
import { setupI18n } from "../i18n";
|
||||||
import GraphsPage from "./GraphsPage.svelte";
|
import GraphsPage from "./GraphsPage.svelte";
|
||||||
|
|
||||||
export function graphs(target: HTMLDivElement) {
|
export function graphs(target: HTMLDivElement): void {
|
||||||
setupI18n().then((i18n) => {
|
setupI18n().then((i18n) => {
|
||||||
new GraphsPage({
|
new GraphsPage({
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function histogramGraph(
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
data: HistogramData
|
data: HistogramData
|
||||||
): void {
|
): void {
|
||||||
const binValue = data.binValue ?? ((bin: any) => bin.length as number);
|
const binValue = data.binValue ?? ((bin: any): number => bin.length as number);
|
||||||
|
|
||||||
const svg = select(svgElem);
|
const svg = select(svgElem);
|
||||||
const trans = svg.transition().duration(600) as any;
|
const trans = svg.transition().duration(600) as any;
|
||||||
|
|
|
@ -111,8 +111,7 @@ export function renderReviews(
|
||||||
bounds: GraphBounds,
|
bounds: GraphBounds,
|
||||||
sourceData: GraphData,
|
sourceData: GraphData,
|
||||||
range: ReviewRange,
|
range: ReviewRange,
|
||||||
showTime: boolean,
|
showTime: boolean
|
||||||
tooltipArea: HTMLDivElement
|
|
||||||
): void {
|
): void {
|
||||||
const xMax = 0;
|
const xMax = 0;
|
||||||
let xMin = 0;
|
let xMin = 0;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import { I18n } from "./i18n";
|
import { I18n } from "./i18n";
|
||||||
import { FluentNumber } from "@fluent/bundle";
|
|
||||||
|
|
||||||
const SECOND = 1.0;
|
const SECOND = 1.0;
|
||||||
const MINUTE = 60.0 * SECOND;
|
const MINUTE = 60.0 * SECOND;
|
||||||
|
|
Loading…
Reference in a new issue