mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-21 00:11:27 -07:00
Fix stats command flow and tracking metrics regressions
- Route default `subminer stats` through attached `--stats`; keep daemon path for `--background`/`--stop` - Update overview metrics: lookup rate uses lifetime Yomitan lookups per 100 tokens; new words dedupe by headword - Suppress repeated macOS `Overlay loading...` OSD during fullscreen tracker flaps and improve session-detail chart scaling - Add/adjust launcher, tracker query, stats server, IPC, overlay, and stats UI regression tests; add changelog fragments
This commit is contained in:
@@ -35,6 +35,8 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
|
||||
lookupCount: 10,
|
||||
lookupHits: 8,
|
||||
yomitanLookupCount: 0,
|
||||
knownWordsSeen: 10,
|
||||
knownWordRate: 12.5,
|
||||
},
|
||||
];
|
||||
const rollups: DailyRollup[] = [
|
||||
@@ -66,6 +68,8 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
|
||||
totalCards: 9,
|
||||
totalLookupCount: 100,
|
||||
totalLookupHits: 80,
|
||||
totalTokensSeen: 1000,
|
||||
totalYomitanLookupCount: 23,
|
||||
newWordsToday: 5,
|
||||
newWordsThisWeek: 20,
|
||||
},
|
||||
@@ -80,7 +84,10 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
|
||||
assert.equal(summary.allTimeMinutes, 50);
|
||||
assert.equal(summary.activeDays, 2);
|
||||
assert.equal(summary.totalSessions, 15);
|
||||
assert.equal(summary.lookupRate, 80);
|
||||
assert.deepEqual(summary.lookupRate, {
|
||||
shortValue: '2.3 / 100 tokens',
|
||||
longValue: '2.3 lookups per 100 tokens',
|
||||
});
|
||||
});
|
||||
|
||||
test('buildOverviewSummary prefers lifetime totals from hints when provided', () => {
|
||||
@@ -104,6 +111,8 @@ test('buildOverviewSummary prefers lifetime totals from hints when provided', ()
|
||||
lookupCount: 1,
|
||||
lookupHits: 1,
|
||||
yomitanLookupCount: 0,
|
||||
knownWordsSeen: 2,
|
||||
knownWordRate: 20,
|
||||
},
|
||||
],
|
||||
rollups: [
|
||||
@@ -132,6 +141,8 @@ test('buildOverviewSummary prefers lifetime totals from hints when provided', ()
|
||||
totalCards: 5,
|
||||
totalLookupCount: 0,
|
||||
totalLookupHits: 0,
|
||||
totalTokensSeen: 0,
|
||||
totalYomitanLookupCount: 0,
|
||||
newWordsToday: 0,
|
||||
newWordsThisWeek: 0,
|
||||
},
|
||||
@@ -141,6 +152,7 @@ test('buildOverviewSummary prefers lifetime totals from hints when provided', ()
|
||||
assert.equal(summary.totalTrackedCards, 5);
|
||||
assert.equal(summary.allTimeMinutes, 120);
|
||||
assert.equal(summary.activeDays, 40);
|
||||
assert.equal(summary.lookupRate, null);
|
||||
});
|
||||
|
||||
test('buildVocabularySummary treats firstSeen timestamps as seconds', () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
VocabularyEntry,
|
||||
} from '../types/stats';
|
||||
import { epochDayToDate, epochMsFromDbTimestamp, localDayFromMs } from './formatters';
|
||||
import { buildLookupRateDisplay, type LookupRateDisplay } from './yomitan-lookup';
|
||||
|
||||
export interface ChartPoint {
|
||||
label: string;
|
||||
@@ -25,7 +26,7 @@ export interface OverviewSummary {
|
||||
averageSessionMinutes: number;
|
||||
activeDays: number;
|
||||
totalSessions: number;
|
||||
lookupRate: number | null;
|
||||
lookupRate: LookupRateDisplay | null;
|
||||
todayTokens: number;
|
||||
newWordsToday: number;
|
||||
newWordsThisWeek: number;
|
||||
@@ -181,10 +182,10 @@ export function buildOverviewSummary(
|
||||
: 0,
|
||||
activeDays: overview.hints.activeDays ?? daysWithActivity.size,
|
||||
totalSessions: overview.hints.totalSessions ?? overview.sessions.length,
|
||||
lookupRate:
|
||||
overview.hints.totalLookupCount > 0
|
||||
? Math.round((overview.hints.totalLookupHits / overview.hints.totalLookupCount) * 100)
|
||||
: null,
|
||||
lookupRate: buildLookupRateDisplay(
|
||||
overview.hints.totalYomitanLookupCount,
|
||||
overview.hints.totalTokensSeen,
|
||||
),
|
||||
todayTokens: Math.max(
|
||||
todayRow?.words ?? 0,
|
||||
sumBy(todaySessions, (session) => session.tokensSeen),
|
||||
|
||||
@@ -23,6 +23,8 @@ test('MediaSessionList renders expandable session rows with delete affordance',
|
||||
lookupCount: 3,
|
||||
lookupHits: 2,
|
||||
yomitanLookupCount: 1,
|
||||
knownWordsSeen: 6,
|
||||
knownWordRate: 25,
|
||||
},
|
||||
]}
|
||||
onDeleteSession={() => {}}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { SessionDetail } from '../components/sessions/SessionDetail';
|
||||
import {
|
||||
SessionDetail,
|
||||
getKnownPctAxisMax,
|
||||
} from '../components/sessions/SessionDetail';
|
||||
import { buildSessionChartEvents } from './session-events';
|
||||
import { EventType } from '../types/stats';
|
||||
|
||||
@@ -24,6 +27,8 @@ test('SessionDetail omits the misleading new words metric', () => {
|
||||
lookupCount: 0,
|
||||
lookupHits: 0,
|
||||
yomitanLookupCount: 0,
|
||||
knownWordsSeen: 0,
|
||||
knownWordRate: 0,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
@@ -58,3 +63,11 @@ test('buildSessionChartEvents keeps only chart-relevant events and pairs pause r
|
||||
);
|
||||
assert.deepEqual(chartEvents.pauseRegions, [{ startMs: 2_000, endMs: 4_000 }]);
|
||||
});
|
||||
|
||||
test('getKnownPctAxisMax adds headroom above the highest known percentage', () => {
|
||||
assert.equal(getKnownPctAxisMax([22.4, 31.2, 29.8]), 40);
|
||||
});
|
||||
|
||||
test('getKnownPctAxisMax caps the chart top at 100%', () => {
|
||||
assert.equal(getKnownPctAxisMax([97.1, 98.6]), 100);
|
||||
});
|
||||
|
||||
@@ -157,6 +157,8 @@ test('SessionRow prefers token-based word count when available', () => {
|
||||
lookupCount: 0,
|
||||
lookupHits: 0,
|
||||
yomitanLookupCount: 0,
|
||||
knownWordsSeen: 0,
|
||||
knownWordRate: 0,
|
||||
}}
|
||||
isExpanded={false}
|
||||
detailsId="session-7"
|
||||
|
||||
Reference in New Issue
Block a user