From 88a3fd8d7b634930706f6dbdc6cdc7563a9bf70f Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 2 Aug 2021 16:47:05 +1000 Subject: [PATCH] support updating multiple cards in one transaction/undo op --- proto/anki/cards.proto | 6 +++--- pylib/anki/cards.py | 4 ++-- pylib/anki/collection.py | 11 +++++++++-- rslib/src/backend/card.rs | 10 +++++++--- rslib/src/card/mod.rs | 16 +++++++++++----- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/proto/anki/cards.proto b/proto/anki/cards.proto index 26b260982..c16033c79 100644 --- a/proto/anki/cards.proto +++ b/proto/anki/cards.proto @@ -10,7 +10,7 @@ import "anki/collection.proto"; service CardsService { rpc GetCard(CardId) returns (Card); - rpc UpdateCard(UpdateCardRequest) returns (collection.OpChanges); + rpc UpdateCards(UpdateCardsRequest) returns (collection.OpChanges); rpc RemoveCards(RemoveCardsRequest) returns (generic.Empty); rpc SetDeck(SetDeckRequest) returns (collection.OpChangesWithCount); rpc SetFlag(SetFlagRequest) returns (collection.OpChangesWithCount); @@ -44,8 +44,8 @@ message Card { string data = 18; } -message UpdateCardRequest { - Card card = 1; +message UpdateCardsRequest { + repeated Card cards = 1; bool skip_undo_entry = 2; } diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index 769332f8d..2f7a50f18 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -114,8 +114,8 @@ class Card(DeprecatedNamesMixin): def flush(self) -> None: hooks.card_will_flush(self) if self.id != 0: - self.col._backend.update_card( - card=self._to_backend_card(), skip_undo_entry=True + self.col._backend.update_cards( + cards=[self._to_backend_card()], skip_undo_entry=True ) else: raise Exception("card.flush() expects an existing card") diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 47c73fc44..ca250c77f 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -314,10 +314,17 @@ class Collection(DeprecatedNamesMixin): def get_card(self, id: CardId) -> Card: return Card(self, id) - def update_card(self, card: Card) -> None: + def update_cards(self, cards: Sequence[Card]) -> OpChanges: """Save card changes to database, and add an undo entry. Unlike card.flush(), this will invalidate any current checkpoint.""" - self._backend.update_card(card=card._to_backend_card(), skip_undo_entry=False) + return self._backend.update_cards( + cards=[c._to_backend_card() for c in cards], skip_undo_entry=False + ) + + def update_card(self, card: Card) -> OpChanges: + """Save card changes to database, and add an undo entry. + Unlike card.flush(), this will invalidate any current checkpoint.""" + return self.update_cards([card]) def get_note(self, id: NoteId) -> Note: return Note(self, id=id) diff --git a/rslib/src/backend/card.rs b/rslib/src/backend/card.rs index c377ae6be..72d3e1bfb 100644 --- a/rslib/src/backend/card.rs +++ b/rslib/src/backend/card.rs @@ -21,10 +21,14 @@ impl CardsService for Backend { }) } - fn update_card(&self, input: pb::UpdateCardRequest) -> Result { + fn update_cards(&self, input: pb::UpdateCardsRequest) -> Result { self.with_col(|col| { - let mut card: Card = input.card.ok_or(AnkiError::NotFound)?.try_into()?; - col.update_card_maybe_undoable(&mut card, !input.skip_undo_entry) + let cards = input + .cards + .into_iter() + .map(TryInto::try_into) + .collect::, AnkiError>>()?; + col.update_cards_maybe_undoable(cards, !input.skip_undo_entry) }) .map(Into::into) } diff --git a/rslib/src/card/mod.rs b/rslib/src/card/mod.rs index 27ad0253a..cc7102c90 100644 --- a/rslib/src/card/mod.rs +++ b/rslib/src/card/mod.rs @@ -159,19 +159,25 @@ impl Card { } impl Collection { - pub(crate) fn update_card_maybe_undoable( + pub(crate) fn update_cards_maybe_undoable( &mut self, - card: &mut Card, + cards: Vec, undoable: bool, ) -> Result> { - let existing = self.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?; if undoable { self.transact(Op::UpdateCard, |col| { - col.update_card_inner(card, existing, col.usn()?) + for mut card in cards { + let existing = col.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?; + col.update_card_inner(&mut card, existing, col.usn()?)? + } + Ok(()) }) } else { self.transact_no_undo(|col| { - col.update_card_inner(card, existing, col.usn()?)?; + for mut card in cards { + let existing = col.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?; + col.update_card_inner(&mut card, existing, col.usn()?)?; + } Ok(OpOutput { output: (), changes: OpChanges {