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 {
|
||||
// CardsGraphData cards = 1;
|
||||
// TodayGraphData today = 3;
|
||||
// ButtonsGraphData buttons = 4;
|
||||
// repeated HourGraphData hours = 2;
|
||||
repeated Card cards = 1;
|
||||
repeated RevlogEntry revlog = 2;
|
||||
|
||||
uint32 days_elapsed = 3;
|
||||
// Based on rollover hour
|
||||
uint32 next_day_at_secs = 4;
|
||||
uint32 scheduler_version = 5;
|
||||
/// Seconds to add to UTC timestamps to get local time.
|
||||
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 {
|
||||
|
|
|
@ -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-mature-cards = No mature cards were studied today.
|
||||
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,
|
||||
scheduler_version: self.sched_ver() as u32,
|
||||
local_offset_secs: local_offset_secs as u32,
|
||||
note_count: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,18 @@
|
|||
import pb from "./backend/proto";
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(args)) {
|
||||
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[] = [];
|
||||
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);
|
||||
const key = this.keyName(id);
|
||||
for (const bundle of this.bundles) {
|
||||
|
|
|
@ -1,17 +1,38 @@
|
|||
<script lang="typescript">
|
||||
import { gatherData, CardCounts } from "./card-counts";
|
||||
import pb from "../backend/proto";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
export let sourceData: pb.BackendProto.GraphsOut | null = null;
|
||||
export let i18n: I18n;
|
||||
|
||||
let cardCounts: CardCounts | null = null;
|
||||
$: if (sourceData) {
|
||||
console.log("gathering data");
|
||||
cardCounts = gatherData(sourceData);
|
||||
cardCounts = gatherData(sourceData, i18n);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="graph">
|
||||
<h1>Card Counts</h1>
|
||||
{JSON.stringify(cardCounts)}
|
||||
</div>
|
||||
<style>
|
||||
.counts-outer {
|
||||
display: flex;
|
||||
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" />
|
||||
|
||||
<TodayStats {sourceData} {i18n} />
|
||||
<CardCounts {sourceData} />
|
||||
<CardCounts {sourceData} {i18n} />
|
||||
<FutureDue {sourceData} />
|
||||
<ReviewsGraph {sourceData} {revlogRange} />
|
||||
<IntervalsGraph {sourceData} />
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
let svg = null as HTMLElement | SVGElement | null;
|
||||
let range: ReviewRange;
|
||||
let showTime = false;
|
||||
let tooltip = null as null | HTMLDivElement;
|
||||
|
||||
$: switch (revlogRange as RevlogRange) {
|
||||
case RevlogRange.Month:
|
||||
|
@ -38,7 +37,7 @@
|
|||
}
|
||||
|
||||
$: if (graphData) {
|
||||
renderReviews(svg as SVGElement, bounds, graphData, range, showTime, tooltip);
|
||||
renderReviews(svg as SVGElement, bounds, graphData, range, showTime);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -83,6 +82,4 @@
|
|||
<AxisLabels {bounds} {xText} {yText} />
|
||||
</svg>
|
||||
|
||||
<div bind:this={tooltip} class="tooltip-area" />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -3,19 +3,15 @@
|
|||
|
||||
import pb from "../backend/proto";
|
||||
import { CardQueue } from "../cards";
|
||||
import { I18n } from "../i18n";
|
||||
|
||||
type Count = [string, number];
|
||||
export interface CardCounts {
|
||||
totalCards: number;
|
||||
totalNotes: number;
|
||||
newCards: number;
|
||||
young: number;
|
||||
mature: number;
|
||||
suspended: number;
|
||||
buried: number;
|
||||
title: string;
|
||||
counts: Count[];
|
||||
}
|
||||
|
||||
export function gatherData(data: pb.BackendProto.GraphsOut): CardCounts {
|
||||
const totalNotes = data.noteCount;
|
||||
export function gatherData(data: pb.BackendProto.GraphsOut, i18n: I18n): CardCounts {
|
||||
const totalCards = data.cards.length;
|
||||
let newCards = 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 {
|
||||
totalCards,
|
||||
totalNotes,
|
||||
newCards,
|
||||
young,
|
||||
mature,
|
||||
suspended,
|
||||
buried,
|
||||
title: i18n.tr(i18n.TR.STATISTICS_COUNTS_TITLE),
|
||||
counts,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
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 { CardQueue } from "../cards";
|
||||
import { HistogramData } from "./histogram-graph";
|
||||
|
@ -65,7 +65,7 @@ export function buildHistogram(
|
|||
return null;
|
||||
}
|
||||
|
||||
const [xMinOrig, origXMax] = extent<number>(data.keys());
|
||||
const [_xMinOrig, origXMax] = extent<number>(data.keys());
|
||||
const xMin = 0;
|
||||
let xMax = origXMax;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { setupI18n } from "../i18n";
|
||||
import GraphsPage from "./GraphsPage.svelte";
|
||||
|
||||
export function graphs(target: HTMLDivElement) {
|
||||
export function graphs(target: HTMLDivElement): void {
|
||||
setupI18n().then((i18n) => {
|
||||
new GraphsPage({
|
||||
target,
|
||||
|
|
|
@ -35,7 +35,7 @@ export function histogramGraph(
|
|||
bounds: GraphBounds,
|
||||
data: HistogramData
|
||||
): 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 trans = svg.transition().duration(600) as any;
|
||||
|
|
|
@ -111,8 +111,7 @@ export function renderReviews(
|
|||
bounds: GraphBounds,
|
||||
sourceData: GraphData,
|
||||
range: ReviewRange,
|
||||
showTime: boolean,
|
||||
tooltipArea: HTMLDivElement
|
||||
showTime: boolean
|
||||
): void {
|
||||
const xMax = 0;
|
||||
let xMin = 0;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import { I18n } from "./i18n";
|
||||
import { FluentNumber } from "@fluent/bundle";
|
||||
|
||||
const SECOND = 1.0;
|
||||
const MINUTE = 60.0 * SECOND;
|
||||
|
|
Loading…
Reference in a new issue