Don't rely on frontend to cap time taken in v3

This commit is contained in:
Damien Elmes 2022-04-02 20:06:23 +10:00
parent 7bfd7245e0
commit d90608198f
6 changed files with 24 additions and 13 deletions

View File

@ -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__)

View File

@ -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

View File

@ -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:

View File

@ -182,7 +182,7 @@ impl SchedulerService for Backend {
}
fn answer_card(&self, input: pb::CardAnswer) -> Result<pb::OpChanges> {
self.with_col(|col| col.answer_card(&input.into()))
self.with_col(|col| col.answer_card(&mut input.into()))
.map(Into::into)
}

View File

@ -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<OpOutput<()>> {
/// Provided [CardAnswer] has its answer time capped to deck preset.
pub fn answer_card(&mut self, answer: &mut CardAnswer) -> Result<OpOutput<()>> {
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,

View File

@ -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,