mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 07:22:23 -04:00
Fix Cards with Missing Last Review Time During Database Check
This commit is contained in:
parent
d13c117e80
commit
d71a3c7413
6 changed files with 44 additions and 11 deletions
|
@ -5,6 +5,11 @@ database-check-card-properties =
|
|||
[one] Fixed { $count } invalid card property.
|
||||
*[other] Fixed { $count } invalid card properties.
|
||||
}
|
||||
database-check-card-last-review-time-empty =
|
||||
{ $count ->
|
||||
[one] Fixed { $count } card with no last review time.
|
||||
*[other] Fixed { $count } cards with no last review time.
|
||||
}
|
||||
database-check-missing-templates =
|
||||
{ $count ->
|
||||
[one] Deleted { $count } card with missing template.
|
||||
|
|
|
@ -40,6 +40,7 @@ pub struct CheckDatabaseOutput {
|
|||
notetypes_recovered: usize,
|
||||
invalid_utf8: usize,
|
||||
invalid_ids: usize,
|
||||
card_last_review_time_empty: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
|
@ -69,6 +70,11 @@ impl CheckDatabaseOutput {
|
|||
if self.card_properties_invalid > 0 {
|
||||
probs.push(tr.database_check_card_properties(self.card_properties_invalid));
|
||||
}
|
||||
if self.card_last_review_time_empty > 0 {
|
||||
probs.push(
|
||||
tr.database_check_card_last_review_time_empty(self.card_last_review_time_empty),
|
||||
);
|
||||
}
|
||||
if self.cards_missing_note > 0 {
|
||||
probs.push(tr.database_check_card_missing_note(self.cards_missing_note));
|
||||
}
|
||||
|
@ -158,7 +164,7 @@ impl Collection {
|
|||
|
||||
fn check_card_properties(&mut self, out: &mut CheckDatabaseOutput) -> Result<()> {
|
||||
let timing = self.timing_today()?;
|
||||
let (new_cnt, other_cnt) = self.storage.fix_card_properties(
|
||||
let (new_cnt, other_cnt, last_review_time_cnt) = self.storage.fix_card_properties(
|
||||
timing.days_elapsed,
|
||||
TimestampSecs::now(),
|
||||
self.usn()?,
|
||||
|
@ -166,6 +172,7 @@ impl Collection {
|
|||
)?;
|
||||
out.card_position_too_high = new_cnt;
|
||||
out.card_properties_invalid += other_cnt;
|
||||
out.card_last_review_time_empty = last_review_time_cnt;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -84,6 +84,16 @@ impl RevlogEntry {
|
|||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns true if the review entry is not manually rescheduled and not
|
||||
/// cramming. Used to filter out entries that shouldn't be considered
|
||||
/// for statistics and scheduling.
|
||||
pub(crate) fn has_rating_and_affect_scheduling(&self) -> bool {
|
||||
// not rescheduled/set due date/reset
|
||||
self.button_chosen > 0
|
||||
// not cramming
|
||||
&& (self.review_kind != RevlogReviewKind::Filtered || self.ease_factor != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
|
|
|
@ -306,15 +306,15 @@ pub(crate) fn fsrs_items_for_memory_states(
|
|||
.collect()
|
||||
}
|
||||
|
||||
struct LastRevlogInfo {
|
||||
pub(crate) struct LastRevlogInfo {
|
||||
/// Used to determine the actual elapsed time between the last time the user
|
||||
/// reviewed the card and now, so that we can determine an accurate period
|
||||
/// when the card has subsequently been rescheduled to a different day.
|
||||
last_reviewed_at: Option<TimestampSecs>,
|
||||
pub(crate) last_reviewed_at: Option<TimestampSecs>,
|
||||
}
|
||||
|
||||
/// Return a map of cards to info about last review/reschedule.
|
||||
fn get_last_revlog_info(revlogs: &[RevlogEntry]) -> HashMap<CardId, LastRevlogInfo> {
|
||||
pub(crate) fn get_last_revlog_info(revlogs: &[RevlogEntry]) -> HashMap<CardId, LastRevlogInfo> {
|
||||
let mut out = HashMap::new();
|
||||
revlogs
|
||||
.iter()
|
||||
|
@ -323,7 +323,7 @@ fn get_last_revlog_info(revlogs: &[RevlogEntry]) -> HashMap<CardId, LastRevlogIn
|
|||
.for_each(|(card_id, group)| {
|
||||
let mut last_reviewed_at = None;
|
||||
for e in group.into_iter() {
|
||||
if e.button_chosen >= 1 {
|
||||
if e.has_rating_and_affect_scheduling() {
|
||||
last_reviewed_at = Some(e.id.as_secs());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,10 +53,7 @@ impl GraphsContext {
|
|||
self.revlog
|
||||
.iter()
|
||||
.filter(|review| {
|
||||
// not rescheduled/set due date/reset
|
||||
review.button_chosen > 0
|
||||
// not cramming
|
||||
&& (review.review_kind != RevlogReviewKind::Filtered || review.ease_factor != 0)
|
||||
review.has_rating_and_affect_scheduling()
|
||||
// cards with an interval ≥ 1 day
|
||||
&& (review.review_kind == RevlogReviewKind::Review
|
||||
|| review.last_interval <= -86400
|
||||
|
|
|
@ -33,6 +33,7 @@ use crate::decks::DeckKind;
|
|||
use crate::error::Result;
|
||||
use crate::notes::NoteId;
|
||||
use crate::scheduler::congrats::CongratsInfo;
|
||||
use crate::scheduler::fsrs::memory_state::get_last_revlog_info;
|
||||
use crate::scheduler::queue::BuryMode;
|
||||
use crate::scheduler::queue::DueCard;
|
||||
use crate::scheduler::queue::DueCardKind;
|
||||
|
@ -365,7 +366,7 @@ impl super::SqliteStorage {
|
|||
mtime: TimestampSecs,
|
||||
usn: Usn,
|
||||
v1_sched: bool,
|
||||
) -> Result<(usize, usize)> {
|
||||
) -> Result<(usize, usize, usize)> {
|
||||
let new_cnt = self
|
||||
.db
|
||||
.prepare(include_str!("fix_due_new.sql"))?
|
||||
|
@ -390,7 +391,20 @@ impl super::SqliteStorage {
|
|||
.db
|
||||
.prepare(include_str!("fix_ordinal.sql"))?
|
||||
.execute(params![mtime, usn])?;
|
||||
Ok((new_cnt, other_cnt))
|
||||
let mut last_review_time_cnt = 0;
|
||||
let revlog = self.get_all_revlog_entries_in_card_order()?;
|
||||
let last_revlog_info = get_last_revlog_info(&revlog);
|
||||
for (card_id, last_revlog_info) in last_revlog_info {
|
||||
let card = self.get_card(card_id)?;
|
||||
if let Some(mut card) = card {
|
||||
if card.ctype != CardType::New && card.last_review_time.is_none() {
|
||||
card.last_review_time = last_revlog_info.last_reviewed_at;
|
||||
self.update_card(&mut card)?;
|
||||
last_review_time_cnt += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((new_cnt, other_cnt, last_review_time_cnt))
|
||||
}
|
||||
|
||||
pub(crate) fn delete_orphaned_cards(&self) -> Result<usize> {
|
||||
|
|
Loading…
Reference in a new issue