add item stats

This commit is contained in:
Michael Zhang 2023-06-14 13:49:11 -05:00
parent 41016c184d
commit b2ff5d8416
7 changed files with 221 additions and 95 deletions

View file

@ -1,4 +1,4 @@
use std::{path::PathBuf, time::Duration}; use std::{collections::HashMap, path::PathBuf, time::Duration};
use sqlx::{Row, SqlitePool}; use sqlx::{Row, SqlitePool};
use tauri::State; use tauri::State;
@ -18,10 +18,12 @@ pub struct SrsStats {
total_items: u32, total_items: u32,
total_reviews: u32, total_reviews: u32,
next_review: Option<i64>,
grades: HashMap<u32, u32>,
/// Used to calculate average success /// Used to calculate average success
num_success: u32, num_success: u32,
num_failure: u32, num_failure: u32,
next_review: Option<i64>,
} }
#[tauri::command] #[tauri::command]
@ -65,6 +67,21 @@ pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result<SrsStats, String> {
.await .await
.map_err(|err| err.to_string())?; .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::<u32, _>(0), row.get::<u32, _>(1)))
.collect::<HashMap<_, _>>();
let next_review = row let next_review = row
.try_get::<i64, _>("next_review") .try_get::<i64, _>("next_review")
.ok() .ok()
@ -78,6 +95,7 @@ pub async fn get_srs_stats(db: State<'_, SrsDb>) -> Result<SrsStats, String> {
num_success: row.try_get("num_success").unwrap_or(0), num_success: row.try_get("num_success").unwrap_or(0),
num_failure: row.try_get("num_failure").unwrap_or(0), num_failure: row.try_get("num_failure").unwrap_or(0),
next_review, next_review,
grades,
}) })
} }

View file

@ -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;
}

View file

@ -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 (
<div className={styles.container}>
{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 (
<div style={{ backgroundColor: group.color }} className={styles.group} key={group.name}>
<div className={styles.groupHeader}>
<h1>{groupCount}</h1>
{group.name}
</div>
<div className={styles.groupLevels}>
{groupLevels.map((level) => (
<div className={styles.level} key={level.name}>
<h3>{level.name}</h3>
{grades.get(level.value)}
</div>
))}
</div>
</div>
);
})}
</div>
);
}

View file

@ -7,18 +7,10 @@ import { Link } from "react-router-dom";
import ConditionalWrapper from "./utils/ConditionalWrapper"; import ConditionalWrapper from "./utils/ConditionalWrapper";
import ReactTimeago, { Formatter } from "react-timeago"; import ReactTimeago, { Formatter } from "react-timeago";
import { isValid } from "date-fns"; import { isValid } from "date-fns";
import { SrsStats } from "../panes/SrsPane";
interface SrsStats { export interface DashboardReviewStatsProps {
reviews_available: number; srsStats: SrsStats;
reviews_today: number;
total_items: number;
total_reviews: number;
next_review: number;
/// Used to calculate average success
num_success: number;
num_failure: number;
} }
interface Stat { interface Stat {
@ -26,21 +18,7 @@ interface Stat {
value: any; value: any;
} }
export default function DashboardReviewStats() { export default function DashboardReviewStats({ srsStats }: DashboardReviewStatsProps) {
const {
data: srsStats,
error,
isLoading,
} = useSWR(["get_srs_stats"], ([command]) => invoke<SrsStats>(command));
if (!srsStats)
return (
<>
{JSON.stringify([srsStats, error, isLoading])}
Loading...
</>
);
const averageSuccess = srsStats.num_success / (srsStats.num_success + srsStats.num_failure || 1); const averageSuccess = srsStats.num_success / (srsStats.num_success + srsStats.num_failure || 1);
const averageSuccessStr = `${Math.round(averageSuccess * 10000) / 100}%`; const averageSuccessStr = `${Math.round(averageSuccess * 10000) / 100}%`;

View file

@ -1,69 +1,78 @@
[ {
{ "groups": [
"group": "Set In Stone", { "name": "Discovering", "color": "#1A814D" },
"name": "★", { "name": "Committing", "color": "#004E8E" },
"value": 8, { "name": "Bolstering", "color": "#5C1696" },
"delay": null, { "name": "Assimilating", "color": "#890062" },
"color": "#1C1C1C" { "name": "Set In Stone", "color": "#1C1C1C" }
}, ],
"levels": [
{
"group": "Set In Stone",
"name": "★",
"value": 8,
"delay": null,
"color": "#1C1C1C"
},
{ {
"group": "Assimilating", "group": "Assimilating",
"name": "A2", "name": "A2",
"value": 7, "value": 7,
"delay": 10368000, "delay": 10368000,
"color": "#890062" "color": "#890062"
}, },
{ {
"group": "Assimilating", "group": "Assimilating",
"name": "A1", "name": "A1",
"value": 6, "value": 6,
"delay": 2592000, "delay": 2592000,
"color": "#890062" "color": "#890062"
}, },
{ {
"group": "Bolstering", "group": "Bolstering",
"name": "B2", "name": "B2",
"value": 5, "value": 5,
"delay": 1209600, "delay": 1209600,
"color": "#5C1696" "color": "#5C1696"
}, },
{ {
"group": "Bolstering", "group": "Bolstering",
"name": "B1", "name": "B1",
"value": 4, "value": 4,
"delay": 604800, "delay": 604800,
"color": "#5C1696" "color": "#5C1696"
}, },
{ {
"group": "Committing", "group": "Committing",
"name": "C2", "name": "C2",
"value": 3, "value": 3,
"delay": 259200, "delay": 259200,
"color": "#004E8E" "color": "#004E8E"
}, },
{ {
"group": "Committing", "group": "Committing",
"name": "C1", "name": "C1",
"value": 2, "value": 2,
"delay": 86400, "delay": 86400,
"color": "#004E8E" "color": "#004E8E"
}, },
{ {
"group": "Discovering", "group": "Discovering",
"name": "D2", "name": "D2",
"value": 1, "value": 1,
"delay": 28800, "delay": 28800,
"color": "#1A814D" "color": "#1A814D"
}, },
{ {
"group": "Discovering", "group": "Discovering",
"name": "D1", "name": "D1",
"value": 0, "value": 0,
"delay": 14400, "delay": 14400,
"color": "#1A814D" "color": "#1A814D"
} }
] ]
}

View file

@ -16,7 +16,9 @@ export interface SrsLevel {
} }
import srsLevelMap from "../data/srslevels.json"; import srsLevelMap from "../data/srslevels.json";
export const srsLevels: Map<number, SrsLevel> = new Map(srsLevelMap.map((v) => [v.value, v])); export const srsLevels: Map<number, SrsLevel> = new Map(
srsLevelMap.levels.map((v) => [v.value, v]),
);
export interface SrsQuestionGroup { export interface SrsQuestionGroup {
srsEntry: SrsEntry; srsEntry: SrsEntry;

View file

@ -1,12 +1,40 @@
import useSWR from "swr";
import DashboardItemStats from "../components/DashboardItemStats";
import DashboardReviewStats from "../components/DashboardReviewStats"; import DashboardReviewStats from "../components/DashboardReviewStats";
import styles from "./SrsPane.module.scss"; 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() { export function Component() {
const {
data: srsStats,
error,
isLoading,
} = useSWR(["get_srs_stats"], ([command]) => invoke<SrsStats>(command));
if (!srsStats) return <>Loading...</>;
return ( return (
<main className={styles.main}> <main className={styles.main}>
<DashboardReviewStats /> <DashboardReviewStats srsStats={srsStats} />
<hr /> <hr />
<DashboardItemStats srsStats={srsStats} />
</main> </main>
); );
} }