From b2ff5d84161b48886e2d79cddabdf205ec621350 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Wed, 14 Jun 2023 13:49:11 -0500 Subject: [PATCH] add item stats --- src-tauri/src/srs.rs | 22 ++- src/components/DashboardItemStats.module.scss | 40 +++++ src/components/DashboardItemStats.tsx | 51 +++++++ src/components/DashboardReviewStats.tsx | 30 +--- src/data/srslevels.json | 139 ++++++++++-------- src/lib/srs.ts | 4 +- src/panes/SrsPane.tsx | 30 +++- 7 files changed, 221 insertions(+), 95 deletions(-) create mode 100644 src/components/DashboardItemStats.module.scss create mode 100644 src/components/DashboardItemStats.tsx diff --git a/src-tauri/src/srs.rs b/src-tauri/src/srs.rs index c9f3071..2693da0 100644 --- a/src-tauri/src/srs.rs +++ b/src-tauri/src/srs.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, time::Duration}; +use std::{collections::HashMap, path::PathBuf, time::Duration}; use sqlx::{Row, SqlitePool}; use tauri::State; @@ -18,10 +18,12 @@ pub struct SrsStats { total_items: u32, total_reviews: u32, + next_review: Option, + grades: HashMap, + /// Used to calculate average success num_success: u32, num_failure: u32, - next_review: Option, } #[tauri::command] @@ -65,6 +67,21 @@ pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result { .await .map_err(|err| err.to_string())?; + // current grades query + let row3 = sqlx::query( + r#" + SELECT CurrentGrade, COUNT(*) FROM SrsEntrySet + GROUP BY CurrentGrade + "#, + ) + .fetch_all(&db.0) + .await + .map_err(|err| err.to_string())?; + let grades = row3 + .into_iter() + .map(|row| (row.get::(0), row.get::(1))) + .collect::>(); + let next_review = row .try_get::("next_review") .ok() @@ -78,6 +95,7 @@ pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result { num_success: row.try_get("num_success").unwrap_or(0), num_failure: row.try_get("num_failure").unwrap_or(0), next_review, + grades, }) } diff --git a/src/components/DashboardItemStats.module.scss b/src/components/DashboardItemStats.module.scss new file mode 100644 index 0000000..10ef08d --- /dev/null +++ b/src/components/DashboardItemStats.module.scss @@ -0,0 +1,40 @@ +.container { + width: 100%; + display: flex; + gap: 8px; + justify-content: space-between; +} + +.group { + flex-grow: 1; + color: white; + text-align: center; + border-radius: 4px; + padding: 12px; + + display: flex; + flex-direction: column; + gap: 12px; +} + +.groupHeader { + h1 { + font-size: 2.5em; + text-shadow: 1px 1px black; + } +} + +.groupLevels { + display: flex; + gap: 4px; + justify-content: space-evenly; + + h3 { + border: 1px solid rgba(255, 255, 255, 0.5); + background-color: rgba(255, 255, 255, 0.25); + } +} + +.level { + flex-grow: 1; +} diff --git a/src/components/DashboardItemStats.tsx b/src/components/DashboardItemStats.tsx new file mode 100644 index 0000000..6f963de --- /dev/null +++ b/src/components/DashboardItemStats.tsx @@ -0,0 +1,51 @@ +import srsLevels from "../data/srslevels.json"; +import { SrsStats } from "../panes/SrsPane"; +import styles from "./DashboardItemStats.module.scss"; + +export interface DashboardItemStatsProps { + srsStats: SrsStats; +} + +const srsLevelsByGroups = new Map( + srsLevels.groups.map((group) => [ + group, + srsLevels.levels.filter((level) => level.group == group.name), + ]), +); + +export default function DashboardItemStats({ srsStats }: DashboardItemStatsProps) { + const grades = new Map(Object.entries(srsStats.grades).map(([k, v]) => [parseInt(k), v])); + + return ( +
+ {srsLevels.groups.map((group) => { + const groupLevels = srsLevelsByGroups.get(group); + if (!groupLevels) return null; + + const groupCount = groupLevels + .map((level) => grades.get(level.value) ?? 0) + .reduce((a, b) => a + b); + + return ( +
+
+

{groupCount}

+ + {group.name} +
+ +
+ {groupLevels.map((level) => ( +
+

{level.name}

+ + {grades.get(level.value)} +
+ ))} +
+
+ ); + })} +
+ ); +} diff --git a/src/components/DashboardReviewStats.tsx b/src/components/DashboardReviewStats.tsx index 04bf286..7344cb6 100644 --- a/src/components/DashboardReviewStats.tsx +++ b/src/components/DashboardReviewStats.tsx @@ -7,18 +7,10 @@ import { Link } from "react-router-dom"; import ConditionalWrapper from "./utils/ConditionalWrapper"; import ReactTimeago, { Formatter } from "react-timeago"; import { isValid } from "date-fns"; +import { SrsStats } from "../panes/SrsPane"; -interface SrsStats { - reviews_available: number; - - reviews_today: number; - total_items: number; - total_reviews: number; - next_review: number; - - /// Used to calculate average success - num_success: number; - num_failure: number; +export interface DashboardReviewStatsProps { + srsStats: SrsStats; } interface Stat { @@ -26,21 +18,7 @@ interface Stat { value: any; } -export default function DashboardReviewStats() { - const { - data: srsStats, - error, - isLoading, - } = useSWR(["get_srs_stats"], ([command]) => invoke(command)); - - if (!srsStats) - return ( - <> - {JSON.stringify([srsStats, error, isLoading])} - Loading... - - ); - +export default function DashboardReviewStats({ srsStats }: DashboardReviewStatsProps) { const averageSuccess = srsStats.num_success / (srsStats.num_success + srsStats.num_failure || 1); const averageSuccessStr = `${Math.round(averageSuccess * 10000) / 100}%`; diff --git a/src/data/srslevels.json b/src/data/srslevels.json index fcb4030..e8b9877 100644 --- a/src/data/srslevels.json +++ b/src/data/srslevels.json @@ -1,69 +1,78 @@ -[ - { - "group": "Set In Stone", - "name": "★", - "value": 8, - "delay": null, - "color": "#1C1C1C" - }, +{ + "groups": [ + { "name": "Discovering", "color": "#1A814D" }, + { "name": "Committing", "color": "#004E8E" }, + { "name": "Bolstering", "color": "#5C1696" }, + { "name": "Assimilating", "color": "#890062" }, + { "name": "Set In Stone", "color": "#1C1C1C" } + ], + "levels": [ + { + "group": "Set In Stone", + "name": "★", + "value": 8, + "delay": null, + "color": "#1C1C1C" + }, - { - "group": "Assimilating", - "name": "A2", - "value": 7, - "delay": 10368000, - "color": "#890062" - }, - { - "group": "Assimilating", - "name": "A1", - "value": 6, - "delay": 2592000, - "color": "#890062" - }, + { + "group": "Assimilating", + "name": "A2", + "value": 7, + "delay": 10368000, + "color": "#890062" + }, + { + "group": "Assimilating", + "name": "A1", + "value": 6, + "delay": 2592000, + "color": "#890062" + }, - { - "group": "Bolstering", - "name": "B2", - "value": 5, - "delay": 1209600, - "color": "#5C1696" - }, - { - "group": "Bolstering", - "name": "B1", - "value": 4, - "delay": 604800, - "color": "#5C1696" - }, + { + "group": "Bolstering", + "name": "B2", + "value": 5, + "delay": 1209600, + "color": "#5C1696" + }, + { + "group": "Bolstering", + "name": "B1", + "value": 4, + "delay": 604800, + "color": "#5C1696" + }, - { - "group": "Committing", - "name": "C2", - "value": 3, - "delay": 259200, - "color": "#004E8E" - }, - { - "group": "Committing", - "name": "C1", - "value": 2, - "delay": 86400, - "color": "#004E8E" - }, + { + "group": "Committing", + "name": "C2", + "value": 3, + "delay": 259200, + "color": "#004E8E" + }, + { + "group": "Committing", + "name": "C1", + "value": 2, + "delay": 86400, + "color": "#004E8E" + }, - { - "group": "Discovering", - "name": "D2", - "value": 1, - "delay": 28800, - "color": "#1A814D" - }, - { - "group": "Discovering", - "name": "D1", - "value": 0, - "delay": 14400, - "color": "#1A814D" - } -] + { + "group": "Discovering", + "name": "D2", + "value": 1, + "delay": 28800, + "color": "#1A814D" + }, + { + "group": "Discovering", + "name": "D1", + "value": 0, + "delay": 14400, + "color": "#1A814D" + } + ] +} diff --git a/src/lib/srs.ts b/src/lib/srs.ts index 6283869..6ed2ae0 100644 --- a/src/lib/srs.ts +++ b/src/lib/srs.ts @@ -16,7 +16,9 @@ export interface SrsLevel { } import srsLevelMap from "../data/srslevels.json"; -export const srsLevels: Map = new Map(srsLevelMap.map((v) => [v.value, v])); +export const srsLevels: Map = new Map( + srsLevelMap.levels.map((v) => [v.value, v]), +); export interface SrsQuestionGroup { srsEntry: SrsEntry; diff --git a/src/panes/SrsPane.tsx b/src/panes/SrsPane.tsx index f6e8d09..a148a5f 100644 --- a/src/panes/SrsPane.tsx +++ b/src/panes/SrsPane.tsx @@ -1,12 +1,40 @@ +import useSWR from "swr"; +import DashboardItemStats from "../components/DashboardItemStats"; import DashboardReviewStats from "../components/DashboardReviewStats"; import styles from "./SrsPane.module.scss"; +import { invoke } from "@tauri-apps/api"; + +export interface SrsStats { + reviews_available: number; + + reviews_today: number; + total_items: number; + total_reviews: number; + + grades: { string: number }; + next_review: number; + + /// Used to calculate average success + num_success: number; + num_failure: number; +} export function Component() { + const { + data: srsStats, + error, + isLoading, + } = useSWR(["get_srs_stats"], ([command]) => invoke(command)); + + if (!srsStats) return <>Loading...; + return (
- +
+ +
); }