diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index ad66b4f7e..607792514 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -190,10 +190,13 @@ class Card(DeprecatedNamesMixin): "autoplay" ] - def time_taken(self) -> int: - "Time taken to answer card, in integer MS." + def time_taken(self, capped: bool = True) -> int: + """Time taken since card timer started, in integer MS. + If `capped` is true, returned time is limited to deck preset setting.""" total = int((time.time() - self.timer_started) * 1000) - return min(total, self.time_limit()) + if capped: + total = min(total, self.time_limit()) + return total def description(self) -> str: dict_copy = dict(self.__dict__) diff --git a/pylib/anki/scheduler/v2.py b/pylib/anki/scheduler/v2.py index 58f0a05cb..3167ae3f7 100644 --- a/pylib/anki/scheduler/v2.py +++ b/pylib/anki/scheduler/v2.py @@ -493,7 +493,7 @@ limit ?""" card.did, new_delta=new_delta, review_delta=review_delta, - milliseconds_delta=+card.time_taken(), + milliseconds_delta=card.time_taken(), ) # once a card has been answered once, the original due date diff --git a/pylib/anki/scheduler/v3.py b/pylib/anki/scheduler/v3.py index 10b588562..c23d26922 100644 --- a/pylib/anki/scheduler/v3.py +++ b/pylib/anki/scheduler/v3.py @@ -81,7 +81,7 @@ class Scheduler(SchedulerBaseWithLegacy): new_state=new_state, rating=rating, answered_at_millis=int_time(1000), - milliseconds_taken=card.time_taken(), + milliseconds_taken=card.time_taken(capped=False), ) def answer_card(self, input: CardAnswer) -> OpChanges: diff --git a/rslib/src/backend/scheduler/mod.rs b/rslib/src/backend/scheduler/mod.rs index 91024fff2..e3ee09096 100644 --- a/rslib/src/backend/scheduler/mod.rs +++ b/rslib/src/backend/scheduler/mod.rs @@ -182,7 +182,7 @@ impl SchedulerService for Backend { } fn answer_card(&self, input: pb::CardAnswer) -> Result { - self.with_col(|col| col.answer_card(&input.into())) + self.with_col(|col| col.answer_card(&mut input.into())) .map(Into::into) } diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 2eeb66252..6bc229c36 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -43,6 +43,12 @@ pub struct CardAnswer { pub milliseconds_taken: u32, } +impl CardAnswer { + fn cap_answer_secs(&mut self, max_secs: u32) { + self.milliseconds_taken = self.milliseconds_taken.min(max_secs * 1000); + } +} + /// Holds the information required to determine a given card's /// current state, and to apply a state change to it. struct CardStateUpdater { @@ -238,11 +244,12 @@ impl Collection { } /// Answer card, writing its new state to the database. - pub fn answer_card(&mut self, answer: &CardAnswer) -> Result> { + /// Provided [CardAnswer] has its answer time capped to deck preset. + pub fn answer_card(&mut self, answer: &mut CardAnswer) -> Result> { self.transact(Op::AnswerCard, |col| col.answer_card_inner(answer)) } - fn answer_card_inner(&mut self, answer: &CardAnswer) -> Result<()> { + fn answer_card_inner(&mut self, answer: &mut CardAnswer) -> Result<()> { let card = self .storage .get_card(answer.card_id)? @@ -251,6 +258,7 @@ impl Collection { let usn = self.usn()?; let mut updater = self.card_state_updater(card)?; + answer.cap_answer_secs(updater.config.inner.cap_answer_time_to_secs); let current_state = updater.current_card_state(); if current_state != answer.current_state { return Err(AnkiError::invalid_input(format!( @@ -404,7 +412,7 @@ pub mod test_helpers { { let queued = self.get_next_card()?.unwrap(); let new_state = get_state(&queued.next_states); - self.answer_card(&CardAnswer { + self.answer_card(&mut CardAnswer { card_id: queued.card.id, current_state: queued.next_states.current, new_state, diff --git a/rslib/src/scheduler/answering/preview.rs b/rslib/src/scheduler/answering/preview.rs index eff09c5ea..1ef3b6cf5 100644 --- a/rslib/src/scheduler/answering/preview.rs +++ b/rslib/src/scheduler/answering/preview.rs @@ -85,7 +85,7 @@ mod test { )); // use Again on the preview - col.answer_card(&CardAnswer { + col.answer_card(&mut CardAnswer { card_id: c.id, current_state: next.current, new_state: next.again, @@ -99,7 +99,7 @@ mod test { // hard let next = col.get_next_card_states(c.id)?; - col.answer_card(&CardAnswer { + col.answer_card(&mut CardAnswer { card_id: c.id, current_state: next.current, new_state: next.hard, @@ -112,7 +112,7 @@ mod test { // good let next = col.get_next_card_states(c.id)?; - col.answer_card(&CardAnswer { + col.answer_card(&mut CardAnswer { card_id: c.id, current_state: next.current, new_state: next.good, @@ -125,7 +125,7 @@ mod test { // and then it should return to its old state once easy selected let next = col.get_next_card_states(c.id)?; - col.answer_card(&CardAnswer { + col.answer_card(&mut CardAnswer { card_id: c.id, current_state: next.current, new_state: next.easy,