Feat/Estimated Total Knowledge By Note & Daily Load (#3507)

* Feat/Estimated Total Knowledge By Note & Daily Load

* Update rslib/src/stats/graphs/retrievability.rs

* Update rslib/src/stats/graphs/future_due.rs
This commit is contained in:
Jarrett Ye 2024-10-21 13:19:42 +08:00 committed by GitHub
parent 6ff309e08f
commit b0eb2a2b97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 54 additions and 12 deletions

View file

@ -48,6 +48,11 @@ statistics-cards =
[one] { $cards } card [one] { $cards } card
*[other] { $cards } cards *[other] { $cards } cards
} }
statistics-notes =
{ $notes ->
[one] { $notes } note
*[other] { $notes } notes
}
# a count of how many cards have been answered, eg "Total: 34 reviews" # a count of how many cards have been answered, eg "Total: 34 reviews"
statistics-reviews = statistics-reviews =
{ $reviews -> { $reviews ->
@ -220,6 +225,7 @@ statistics-average-answer-time-label = Average answer time
statistics-average = Average statistics-average = Average
statistics-average-interval = Average interval statistics-average-interval = Average interval
statistics-due-tomorrow = Due tomorrow statistics-due-tomorrow = Due tomorrow
statistics-daily-load = Daily load
# eg 5 of 15 (33.3%) # eg 5 of 15 (33.3%)
statistics-amount-of-total-with-percentage = { $amount } of { $total } ({ $percent }%) statistics-amount-of-total-with-percentage = { $amount } of { $total } ({ $percent }%)
statistics-average-over-period = Average over period statistics-average-over-period = Average over period

View file

@ -85,11 +85,13 @@ message GraphsResponse {
message Retrievability { message Retrievability {
map<uint32, uint32> retrievability = 1; map<uint32, uint32> retrievability = 1;
float average = 2; float average = 2;
float sum = 3; float sum_by_card = 3;
float sum_by_note = 4;
} }
message FutureDue { message FutureDue {
map<int32, uint32> future_due = 1; map<int32, uint32> future_due = 1;
bool have_backlog = 2; bool have_backlog = 2;
uint32 daily_load = 3;
} }
message Today { message Today {
uint32 answer_count = 1; uint32 answer_count = 1;

View file

@ -13,6 +13,7 @@ impl GraphsContext {
pub(super) fn future_due(&self) -> FutureDue { pub(super) fn future_due(&self) -> FutureDue {
let mut have_backlog = false; let mut have_backlog = false;
let mut due_by_day: HashMap<i32, u32> = Default::default(); let mut due_by_day: HashMap<i32, u32> = Default::default();
let mut daily_load = 0.0;
for c in &self.cards { for c in &self.cards {
if matches!(c.queue, CardQueue::New | CardQueue::Suspended) { if matches!(c.queue, CardQueue::New | CardQueue::Suspended) {
continue; continue;
@ -31,10 +32,16 @@ impl GraphsContext {
} }
have_backlog |= due_day < 0; have_backlog |= due_day < 0;
*due_by_day.entry(due_day).or_default() += 1; *due_by_day.entry(due_day).or_default() += 1;
if matches!(c.queue, CardQueue::Review | CardQueue::DayLearn) {
daily_load += 1.0 / c.interval.max(1) as f32;
} else {
daily_load += 1.0;
}
} }
FutureDue { FutureDue {
future_due: due_by_day, future_due: due_by_day,
have_backlog, have_backlog,
daily_load: daily_load as u32,
} }
} }
} }

View file

@ -19,25 +19,37 @@ impl GraphsContext {
next_day_at: Default::default(), next_day_at: Default::default(),
}; };
let fsrs = FSRS::new(None).unwrap(); let fsrs = FSRS::new(None).unwrap();
// note id -> (sum, count)
let mut note_retrievability: std::collections::HashMap<i64, (f32, u32)> =
std::collections::HashMap::new();
for card in &self.cards { for card in &self.cards {
let entry = note_retrievability
.entry(card.note_id.0)
.or_insert((0.0, 0));
entry.1 += 1;
if let Some(state) = card.memory_state { if let Some(state) = card.memory_state {
let r = fsrs.current_retrievability( let elapsed_days = card.days_since_last_review(&timing).unwrap_or_default();
state.into(), let r = fsrs.current_retrievability(state.into(), elapsed_days);
card.days_since_last_review(&timing).unwrap_or_default(),
);
*retrievability *retrievability
.retrievability .retrievability
.entry(percent_to_bin(r * 100.0)) .entry(percent_to_bin(r * 100.0))
.or_insert_with(Default::default) += 1; .or_insert_with(Default::default) += 1;
retrievability.sum += r; retrievability.sum_by_card += r;
card_with_retrievability_count += 1; card_with_retrievability_count += 1;
entry.0 += r;
} else {
entry.0 += 0.0;
} }
} }
if card_with_retrievability_count != 0 { if card_with_retrievability_count != 0 {
retrievability.average = retrievability.average =
retrievability.sum * 100.0 / card_with_retrievability_count as f32; retrievability.sum_by_card * 100.0 / card_with_retrievability_count as f32;
} }
retrievability.sum_by_note = note_retrievability
.values()
.map(|(sum, count)| sum / *count as f32)
.sum();
retrievability retrievability
} }
} }

View file

@ -19,11 +19,16 @@ import type { HistogramData } from "./histogram-graph";
export interface GraphData { export interface GraphData {
dueCounts: Map<number, number>; dueCounts: Map<number, number>;
haveBacklog: boolean; haveBacklog: boolean;
dailyLoad: number;
} }
export function gatherData(data: GraphsResponse): GraphData { export function gatherData(data: GraphsResponse): GraphData {
const msg = data.futureDue!; const msg = data.futureDue!;
return { dueCounts: numericMap(msg.futureDue), haveBacklog: msg.haveBacklog }; return {
dueCounts: numericMap(msg.futureDue),
haveBacklog: msg.haveBacklog,
dailyLoad: msg.dailyLoad,
};
} }
export interface FutureDueResponse { export interface FutureDueResponse {
@ -138,6 +143,12 @@ export function buildHistogram(
reviews: sourceData.dueCounts.get(1) ?? 0, reviews: sourceData.dueCounts.get(1) ?? 0,
}), }),
}, },
{
label: tr.statisticsDailyLoad(),
value: tr.statisticsReviews({
reviews: sourceData.dailyLoad,
}),
},
]; ];
return { return {

View file

@ -18,14 +18,16 @@ import type { HistogramData } from "./histogram-graph";
export interface GraphData { export interface GraphData {
retrievability: Map<number, number>; retrievability: Map<number, number>;
average: number; average: number;
sum: number; sumByCard: number;
sumByNote: number;
} }
export function gatherData(data: GraphsResponse): GraphData { export function gatherData(data: GraphsResponse): GraphData {
return { return {
retrievability: numericMap(data.retrievability!.retrievability), retrievability: numericMap(data.retrievability!.retrievability),
average: data.retrievability!.average, average: data.retrievability!.average,
sum: data.retrievability!.sum, sumByCard: data.retrievability!.sumByCard,
sumByNote: data.retrievability!.sumByNote,
}; };
} }
@ -111,7 +113,9 @@ export function prepareData(
}, },
{ {
label: tr.statisticsEstimatedTotalKnowledge(), label: tr.statisticsEstimatedTotalKnowledge(),
value: tr.statisticsCards({ cards: +data.sum.toFixed(0) }), value: `${tr.statisticsCards({ cards: +data.sumByCard.toFixed(0) })} / ${
tr.statisticsNotes({ notes: +data.sumByNote.toFixed(0) })
}`,
}, },
]; ];