add item stats
This commit is contained in:
parent
41016c184d
commit
b2ff5d8416
7 changed files with 221 additions and 95 deletions
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
src/components/DashboardItemStats.module.scss
Normal file
40
src/components/DashboardItemStats.module.scss
Normal 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;
|
||||||
|
}
|
51
src/components/DashboardItemStats.tsx
Normal file
51
src/components/DashboardItemStats.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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}%`;
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue