mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 03:16:46 -07:00
feat(stats): add episodes completed and anime completed to tracking snapshot
- Query watched videos count and anime with all episodes watched - Display in overview tracking snapshot - Remove efficiency section from trends
This commit is contained in:
@@ -124,6 +124,8 @@ export function getQueryHints(db: DatabaseSync): {
|
||||
activeSessions: number;
|
||||
episodesToday: number;
|
||||
activeAnimeCount: number;
|
||||
totalEpisodesWatched: number;
|
||||
totalAnimeCompleted: number;
|
||||
} {
|
||||
const sessions = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions');
|
||||
const active = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions WHERE ended_at_ms IS NULL');
|
||||
@@ -147,7 +149,23 @@ export function getQueryHints(db: DatabaseSync): {
|
||||
AND s.started_at_ms >= ?
|
||||
`).get(thirtyDaysAgoMs) as { count: number })?.count ?? 0;
|
||||
|
||||
return { totalSessions, activeSessions, episodesToday, activeAnimeCount };
|
||||
const totalEpisodesWatched = (db.prepare(`
|
||||
SELECT COUNT(*) AS count FROM imm_videos WHERE watched = 1
|
||||
`).get() as { count: number })?.count ?? 0;
|
||||
|
||||
const totalAnimeCompleted = (db.prepare(`
|
||||
SELECT COUNT(*) AS count FROM (
|
||||
SELECT a.anime_id
|
||||
FROM imm_anime a
|
||||
JOIN imm_videos v ON v.anime_id = a.anime_id
|
||||
JOIN imm_media_art m ON m.video_id = v.video_id
|
||||
WHERE m.episodes_total IS NOT NULL AND m.episodes_total > 0
|
||||
GROUP BY a.anime_id
|
||||
HAVING COUNT(DISTINCT CASE WHEN v.watched = 1 THEN v.video_id END) >= MAX(m.episodes_total)
|
||||
)
|
||||
`).get() as { count: number })?.count ?? 0;
|
||||
|
||||
return { totalSessions, activeSessions, episodesToday, activeAnimeCount, totalEpisodesWatched, totalAnimeCompleted };
|
||||
}
|
||||
|
||||
export function getDailyRollups(db: DatabaseSync, limit = 60): ImmersionSessionRollupRow[] {
|
||||
|
||||
@@ -40,7 +40,7 @@ export function OverviewTab() {
|
||||
No tracked card-add events in the current immersion DB yet. New cards mined after this fix will show here.
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-5 gap-3 text-sm">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-3 text-sm">
|
||||
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
||||
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Total Sessions</div>
|
||||
<div className="mt-1 text-xl font-semibold text-ctp-lavender">
|
||||
@@ -71,6 +71,18 @@ export function OverviewTab() {
|
||||
{formatNumber(summary.totalTrackedCards)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
||||
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Episodes Completed</div>
|
||||
<div className="mt-1 text-xl font-semibold text-ctp-blue">
|
||||
{formatNumber(summary.totalEpisodesWatched)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-ctp-surface1/60 p-3">
|
||||
<div className="text-xs uppercase tracking-wide text-ctp-overlay2">Anime Completed</div>
|
||||
<div className="mt-1 text-xl font-semibold text-ctp-sapphire">
|
||||
{formatNumber(summary.totalAnimeCompleted)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -141,9 +141,6 @@ export function TrendsTab() {
|
||||
type="line"
|
||||
/>
|
||||
|
||||
<SectionHeader>Efficiency</SectionHeader>
|
||||
<TrendChart title="Cards per Hour" data={dashboard.cardsPerHour} color="#f5a97f" type="line" />
|
||||
|
||||
<SectionHeader>Anime</SectionHeader>
|
||||
<StackedTrendChart title="Anime Progress (episodes)" data={animeProgress} />
|
||||
<StackedTrendChart title="Watch Time per Anime (min)" data={watchTimePerAnime} />
|
||||
|
||||
@@ -49,7 +49,7 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
|
||||
const overview: OverviewData = {
|
||||
sessions,
|
||||
rollups,
|
||||
hints: { totalSessions: 1, activeSessions: 0, episodesToday: 2, activeAnimeCount: 3 },
|
||||
hints: { totalSessions: 1, activeSessions: 0, episodesToday: 2, activeAnimeCount: 3, totalEpisodesWatched: 5, totalAnimeCompleted: 1 },
|
||||
};
|
||||
|
||||
const summary = buildOverviewSummary(overview, now);
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface OverviewSummary {
|
||||
totalTrackedCards: number;
|
||||
episodesToday: number;
|
||||
activeAnimeCount: number;
|
||||
totalEpisodesWatched: number;
|
||||
totalAnimeCompleted: number;
|
||||
averageSessionMinutes: number;
|
||||
totalSessions: number;
|
||||
activeDays: number;
|
||||
@@ -146,6 +148,8 @@ export function buildOverviewSummary(
|
||||
totalTrackedCards: Math.max(sessionCards, rollupCards),
|
||||
episodesToday: overview.hints.episodesToday ?? 0,
|
||||
activeAnimeCount: overview.hints.activeAnimeCount ?? 0,
|
||||
totalEpisodesWatched: overview.hints.totalEpisodesWatched ?? 0,
|
||||
totalAnimeCompleted: overview.hints.totalAnimeCompleted ?? 0,
|
||||
averageSessionMinutes:
|
||||
overview.sessions.length > 0
|
||||
? Math.round(sumBy(overview.sessions, (session) => session.activeWatchedMs) / overview.sessions.length / 60_000)
|
||||
|
||||
@@ -91,6 +91,8 @@ export interface OverviewData {
|
||||
activeSessions: number;
|
||||
episodesToday: number;
|
||||
activeAnimeCount: number;
|
||||
totalEpisodesWatched: number;
|
||||
totalAnimeCompleted: number;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user