mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
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:
parent
6ff309e08f
commit
b0eb2a2b97
6 changed files with 54 additions and 12 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) })
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue