mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
Create a new kind of revlog entry for reschedule cards on change (#3508)
* create a new kind of revlog entry for Reschedule cards on change * add comments * exclude the rescheduled case in reviews graph
This commit is contained in:
parent
30e734b925
commit
26ae51fafd
16 changed files with 76 additions and 24 deletions
|
@ -23,6 +23,7 @@ card-stats-review-log-type-review = Review
|
|||
card-stats-review-log-type-relearn = Relearn
|
||||
card-stats-review-log-type-filtered = Filtered
|
||||
card-stats-review-log-type-manual = Manual
|
||||
card-stats-review-log-type-rescheduled = Rescheduled
|
||||
card-stats-review-log-elapsed-time = Elapsed Time
|
||||
card-stats-no-card = (No card to display.)
|
||||
card-stats-custom-data = Custom Data
|
||||
|
|
|
@ -207,6 +207,7 @@ message RevlogEntry {
|
|||
RELEARNING = 2;
|
||||
FILTERED = 3;
|
||||
MANUAL = 4;
|
||||
RESCHEDULED = 5;
|
||||
}
|
||||
int64 id = 1;
|
||||
int64 cid = 2;
|
||||
|
|
|
@ -72,6 +72,7 @@ pub enum RevlogReviewKind {
|
|||
/// disabled.
|
||||
Filtered = 3,
|
||||
Manual = 4,
|
||||
Rescheduled = 5,
|
||||
}
|
||||
|
||||
impl RevlogEntry {
|
||||
|
@ -86,11 +87,32 @@ impl RevlogEntry {
|
|||
}
|
||||
|
||||
impl Collection {
|
||||
// set due date or reset
|
||||
pub(crate) fn log_manually_scheduled_review(
|
||||
&mut self,
|
||||
card: &Card,
|
||||
original_interval: u32,
|
||||
usn: Usn,
|
||||
) -> Result<()> {
|
||||
self.log_scheduled_review(card, original_interval, usn, RevlogReviewKind::Manual)
|
||||
}
|
||||
|
||||
// reschedule cards on change
|
||||
pub(crate) fn log_rescheduled_review(
|
||||
&mut self,
|
||||
card: &Card,
|
||||
original_interval: u32,
|
||||
usn: Usn,
|
||||
) -> Result<()> {
|
||||
self.log_scheduled_review(card, original_interval, usn, RevlogReviewKind::Rescheduled)
|
||||
}
|
||||
|
||||
fn log_scheduled_review(
|
||||
&mut self,
|
||||
card: &Card,
|
||||
original_interval: u32,
|
||||
usn: Usn,
|
||||
review_kind: RevlogReviewKind,
|
||||
) -> Result<()> {
|
||||
let ease_factor = u32::from(
|
||||
card.memory_state
|
||||
|
@ -106,7 +128,7 @@ impl Collection {
|
|||
last_interval: i32::try_from(original_interval).unwrap_or(i32::MAX),
|
||||
ease_factor,
|
||||
taken_millis: 0,
|
||||
review_kind: RevlogReviewKind::Manual,
|
||||
review_kind,
|
||||
};
|
||||
self.add_revlog_entry_undoable(entry)?;
|
||||
Ok(())
|
||||
|
|
|
@ -119,9 +119,10 @@ impl Collection {
|
|||
};
|
||||
*due = (timing.days_elapsed as i32) - days_elapsed
|
||||
+ card.interval as i32;
|
||||
// Add a manual revlog entry if the last entry wasn't manual
|
||||
if !last_info.last_revlog_is_manual {
|
||||
self.log_manually_scheduled_review(
|
||||
// Add a rescheduled revlog entry if the last entry wasn't
|
||||
// rescheduled
|
||||
if !last_info.last_revlog_is_rescheduled {
|
||||
self.log_rescheduled_review(
|
||||
&card,
|
||||
original_interval,
|
||||
usn,
|
||||
|
@ -237,7 +238,7 @@ struct LastRevlogInfo {
|
|||
last_reviewed_at: Option<TimestampSecs>,
|
||||
/// If true, the last action on this card was a reschedule, so we
|
||||
/// can avoid writing an extra revlog entry on another reschedule.
|
||||
last_revlog_is_manual: bool,
|
||||
last_revlog_is_rescheduled: bool,
|
||||
}
|
||||
|
||||
/// Return a map of cards to info about last review/reschedule.
|
||||
|
@ -249,18 +250,18 @@ fn get_last_revlog_info(revlogs: &[RevlogEntry]) -> HashMap<CardId, LastRevlogIn
|
|||
.into_iter()
|
||||
.for_each(|(card_id, group)| {
|
||||
let mut last_reviewed_at = None;
|
||||
let mut last_revlog_is_manual = false;
|
||||
let mut last_revlog_is_rescheduled = false;
|
||||
for e in group.into_iter() {
|
||||
if e.button_chosen >= 1 {
|
||||
last_reviewed_at = Some(e.id.as_secs());
|
||||
}
|
||||
last_revlog_is_manual = e.review_kind == RevlogReviewKind::Manual;
|
||||
last_revlog_is_rescheduled = e.review_kind == RevlogReviewKind::Rescheduled;
|
||||
}
|
||||
out.insert(
|
||||
card_id,
|
||||
LastRevlogInfo {
|
||||
last_reviewed_at,
|
||||
last_revlog_is_manual,
|
||||
last_revlog_is_rescheduled,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -321,7 +322,7 @@ pub(crate) fn single_card_revlog_to_item(
|
|||
}))
|
||||
}
|
||||
} else {
|
||||
// only manual rescheduling; treat like empty
|
||||
// only manual and rescheduled revlogs; treat like empty
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -84,7 +84,8 @@ impl From<crate::revlog::RevlogReviewKind> for fsrs::RevlogReviewKind {
|
|||
crate::revlog::RevlogReviewKind::Review => fsrs::RevlogReviewKind::Review,
|
||||
crate::revlog::RevlogReviewKind::Relearning => fsrs::RevlogReviewKind::Relearning,
|
||||
crate::revlog::RevlogReviewKind::Filtered => fsrs::RevlogReviewKind::Filtered,
|
||||
crate::revlog::RevlogReviewKind::Manual => fsrs::RevlogReviewKind::Manual,
|
||||
crate::revlog::RevlogReviewKind::Manual
|
||||
| crate::revlog::RevlogReviewKind::Rescheduled => fsrs::RevlogReviewKind::Manual,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -294,6 +294,9 @@ pub(crate) fn single_card_revlog_to_items(
|
|||
Some(RevlogEntry {
|
||||
review_kind: RevlogReviewKind::Manual,
|
||||
..
|
||||
}) | Some(RevlogEntry {
|
||||
review_kind: RevlogReviewKind::Rescheduled,
|
||||
..
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -343,10 +346,12 @@ pub(crate) fn single_card_revlog_to_items(
|
|||
// Filter out unwanted entries
|
||||
entries.retain(|entry| {
|
||||
!(
|
||||
// manually rescheduled
|
||||
// set due date or reset
|
||||
(entry.review_kind == RevlogReviewKind::Manual || entry.button_chosen == 0)
|
||||
|| // cram
|
||||
(entry.review_kind == RevlogReviewKind::Filtered && entry.ease_factor == 0)
|
||||
|| // rescheduled
|
||||
(entry.review_kind == RevlogReviewKind::Rescheduled)
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -407,6 +412,7 @@ fn revlog_entry_to_proto(e: RevlogEntry) -> anki_proto::stats::RevlogEntry {
|
|||
RevlogReviewKind::Relearning => revlog_entry::ReviewKind::Relearning,
|
||||
RevlogReviewKind::Filtered => revlog_entry::ReviewKind::Filtered,
|
||||
RevlogReviewKind::Manual => revlog_entry::ReviewKind::Manual,
|
||||
RevlogReviewKind::Rescheduled => revlog_entry::ReviewKind::Rescheduled,
|
||||
} as i32,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ fn interval_bucket(review: &RevlogEntry) -> Option<IntervalBucket> {
|
|||
} else {
|
||||
IntervalBucket::Mature
|
||||
}),
|
||||
RevlogReviewKind::Manual => None,
|
||||
RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ impl GraphsContext {
|
|||
'outer: for review in &self.revlog {
|
||||
if matches!(
|
||||
review.review_kind,
|
||||
RevlogReviewKind::Filtered | RevlogReviewKind::Manual
|
||||
RevlogReviewKind::Filtered
|
||||
| RevlogReviewKind::Manual
|
||||
| RevlogReviewKind::Rescheduled
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ impl GraphsContext {
|
|||
self.revlog
|
||||
.iter()
|
||||
.filter(|review| {
|
||||
// not manually rescheduled
|
||||
// not rescheduled/set due date/reset
|
||||
review.button_chosen > 0
|
||||
// not cramming
|
||||
&& (review.review_kind != RevlogReviewKind::Filtered || review.ease_factor != 0)
|
||||
|
|
|
@ -10,7 +10,9 @@ impl GraphsContext {
|
|||
pub(super) fn review_counts_and_times(&self) -> ReviewCountsAndTimes {
|
||||
let mut data = ReviewCountsAndTimes::default();
|
||||
for review in &self.revlog {
|
||||
if review.review_kind == RevlogReviewKind::Manual {
|
||||
if review.review_kind == RevlogReviewKind::Manual
|
||||
|| review.review_kind == RevlogReviewKind::Rescheduled
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let day = (review.id.as_secs().elapsed_secs_since(self.next_day_start) / 86_400) as i32;
|
||||
|
@ -38,7 +40,7 @@ impl GraphsContext {
|
|||
count.filtered += 1;
|
||||
time.filtered += review.taken_millis;
|
||||
}
|
||||
RevlogReviewKind::Manual => unreachable!(),
|
||||
RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => unreachable!(),
|
||||
}
|
||||
}
|
||||
data
|
||||
|
|
|
@ -14,7 +14,9 @@ impl GraphsContext {
|
|||
if review.id.0 < start_of_today_ms {
|
||||
continue;
|
||||
}
|
||||
if review.review_kind == RevlogReviewKind::Manual {
|
||||
if review.review_kind == RevlogReviewKind::Manual
|
||||
|| review.review_kind == RevlogReviewKind::Rescheduled
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// total
|
||||
|
@ -37,7 +39,7 @@ impl GraphsContext {
|
|||
RevlogReviewKind::Review => today.review_count += 1,
|
||||
RevlogReviewKind::Relearning => today.relearn_count += 1,
|
||||
RevlogReviewKind::Filtered => today.early_review_count += 1,
|
||||
RevlogReviewKind::Manual => unreachable!(),
|
||||
RevlogReviewKind::Manual | RevlogReviewKind::Rescheduled => unreachable!(),
|
||||
}
|
||||
}
|
||||
today
|
||||
|
|
|
@ -46,6 +46,9 @@ impl From<RevlogReviewKind> for i32 {
|
|||
RevlogReviewKind::Relearning => anki_proto::stats::revlog_entry::ReviewKind::Relearning,
|
||||
RevlogReviewKind::Filtered => anki_proto::stats::revlog_entry::ReviewKind::Filtered,
|
||||
RevlogReviewKind::Manual => anki_proto::stats::revlog_entry::ReviewKind::Manual,
|
||||
RevlogReviewKind::Rescheduled => {
|
||||
anki_proto::stats::revlog_entry::ReviewKind::Rescheduled
|
||||
}
|
||||
}) as i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,12 +172,19 @@ impl SqliteStorage {
|
|||
let start = day_cutoff.adding_secs(-86_400).as_millis();
|
||||
self.db
|
||||
.prepare_cached(include_str!("studied_today.sql"))?
|
||||
.query_map([start.0, RevlogReviewKind::Manual as i64], |row| {
|
||||
Ok(StudiedToday {
|
||||
cards: row.get(0)?,
|
||||
seconds: row.get(1)?,
|
||||
})
|
||||
})?
|
||||
.query_map(
|
||||
[
|
||||
start.0,
|
||||
RevlogReviewKind::Manual as i64,
|
||||
RevlogReviewKind::Rescheduled as i64,
|
||||
],
|
||||
|row| {
|
||||
Ok(StudiedToday {
|
||||
cards: row.get(0)?,
|
||||
seconds: row.get(1)?,
|
||||
})
|
||||
},
|
||||
)?
|
||||
.next()
|
||||
.unwrap()
|
||||
.map_err(Into::into)
|
||||
|
|
|
@ -2,4 +2,5 @@ SELECT COUNT(),
|
|||
coalesce(sum(time) / 1000.0, 0.0)
|
||||
FROM revlog
|
||||
WHERE id > ?
|
||||
AND type != ?
|
||||
AND type != ?
|
|
@ -36,6 +36,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
return tr2.cardStatsReviewLogTypeFiltered();
|
||||
case ReviewKind.MANUAL:
|
||||
return tr2.cardStatsReviewLogTypeManual();
|
||||
case ReviewKind.RESCHEDULED:
|
||||
return tr2.cardStatsReviewLogTypeRescheduled();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ function filterDataByTimeRange(data: DataPoint[], maxDays: number): DataPoint[]
|
|||
export function filterRevlogEntryByReviewKind(entry: RevlogEntry): boolean {
|
||||
return (
|
||||
entry.reviewKind !== RevlogEntry_ReviewKind.MANUAL
|
||||
&& entry.reviewKind !== RevlogEntry_ReviewKind.RESCHEDULED
|
||||
&& (entry.reviewKind !== RevlogEntry_ReviewKind.FILTERED || entry.ease !== 0)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue