mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
move bury/suspend into backend
This commit is contained in:
parent
ac265fe75a
commit
d3dede057a
15 changed files with 176 additions and 90 deletions
|
@ -102,6 +102,7 @@ service BackendService {
|
|||
rpc CongratsInfo (Empty) returns (CongratsInfoOut);
|
||||
rpc RestoreBuriedAndSuspendedCards (CardIDs) returns (Empty);
|
||||
rpc UnburyCardsInCurrentDeck (UnburyCardsInCurrentDeckIn) returns (Empty);
|
||||
rpc BuryOrSuspendCards (BuryOrSuspendCardsIn) returns (Empty);
|
||||
|
||||
// stats
|
||||
|
||||
|
@ -156,6 +157,7 @@ service BackendService {
|
|||
rpc AfterNoteUpdates (AfterNoteUpdatesIn) returns (Empty);
|
||||
rpc FieldNamesForNotes (FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
|
||||
rpc NoteIsDuplicateOrEmpty (Note) returns (NoteIsDuplicateOrEmptyOut);
|
||||
rpc CardsOfNote (NoteID) returns (CardIDs);
|
||||
|
||||
// note types
|
||||
|
||||
|
@ -1042,3 +1044,13 @@ message UnburyCardsInCurrentDeckIn {
|
|||
}
|
||||
Mode mode = 1;
|
||||
}
|
||||
|
||||
message BuryOrSuspendCardsIn {
|
||||
enum Mode {
|
||||
SUSPEND = 0;
|
||||
BURY_SCHED = 1;
|
||||
BURY_USER = 2;
|
||||
}
|
||||
repeated int64 card_ids = 1;
|
||||
Mode mode = 2;
|
||||
}
|
||||
|
|
|
@ -356,6 +356,9 @@ class Collection:
|
|||
hooks.notes_will_be_deleted(self, nids)
|
||||
self.backend.remove_notes(note_ids=[], card_ids=card_ids)
|
||||
|
||||
def card_ids_of_note(self, note_id: int) -> Sequence[int]:
|
||||
return self.backend.cards_of_note(note_id)
|
||||
|
||||
# legacy
|
||||
|
||||
def addNote(self, note: Note) -> int:
|
||||
|
|
|
@ -76,12 +76,10 @@ class Note:
|
|||
return joinFields(self.fields)
|
||||
|
||||
def cards(self) -> List[anki.cards.Card]:
|
||||
return [
|
||||
self.col.getCard(id)
|
||||
for id in self.col.db.list(
|
||||
"select id from cards where nid = ? order by ord", self.id
|
||||
)
|
||||
]
|
||||
return [self.col.getCard(id) for id in self.card_ids()]
|
||||
|
||||
def card_ids(self) -> Sequence[int]:
|
||||
return self.col.card_ids_of_note(self.id)
|
||||
|
||||
def model(self) -> Optional[NoteType]:
|
||||
return self.col.models.get(self.mid)
|
||||
|
|
|
@ -806,32 +806,3 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?"""
|
|||
return self._graduatingIvl(card, conf, False, adj=False) * 86400
|
||||
else:
|
||||
return self._delayForGrade(conf, left)
|
||||
|
||||
# Suspending
|
||||
##########################################################################
|
||||
|
||||
def suspendCards(self, ids: List[int]) -> None:
|
||||
"Suspend cards."
|
||||
self.col.log(ids)
|
||||
self.remFromDyn(ids)
|
||||
self.removeLrn(ids)
|
||||
self.col.db.execute(
|
||||
f"update cards set queue={QUEUE_TYPE_SUSPENDED},mod=?,usn=? where id in "
|
||||
+ ids2str(ids),
|
||||
intTime(),
|
||||
self.col.usn(),
|
||||
)
|
||||
|
||||
def buryCards(self, cids: List[int], manual: bool = False) -> None:
|
||||
# v1 only supported automatic burying
|
||||
assert not manual
|
||||
self.col.log(cids)
|
||||
self.remFromDyn(cids)
|
||||
self.removeLrn(cids)
|
||||
self.col.db.execute(
|
||||
f"""
|
||||
update cards set queue={QUEUE_TYPE_SIBLING_BURIED},mod=?,usn=? where id in """
|
||||
+ ids2str(cids),
|
||||
intTime(),
|
||||
self.col.usn(),
|
||||
)
|
||||
|
|
|
@ -27,6 +27,7 @@ from anki.cards import Card
|
|||
from anki.consts import *
|
||||
from anki.decks import Deck, DeckConfig, DeckManager, FilteredDeck, QueueConfig
|
||||
from anki.lang import _
|
||||
from anki.notes import Note
|
||||
from anki.rsbackend import (
|
||||
CountsForDeckToday,
|
||||
DeckTreeNode,
|
||||
|
@ -36,10 +37,14 @@ from anki.rsbackend import (
|
|||
)
|
||||
from anki.utils import ids2str, intTime
|
||||
|
||||
UnburyCurrentDeckMode = pb.UnburyCardsInCurrentDeckIn.Mode # pylint: disable=no-member
|
||||
UnburyCurrentDeckMode = pb.UnburyCardsInCurrentDeckIn.Mode # pylint:disable=no-member
|
||||
BuryOrSuspendMode = pb.BuryOrSuspendCardsIn.Mode # pylint:disable=no-member
|
||||
if TYPE_CHECKING:
|
||||
UnburyCurrentDeckModeValue = (
|
||||
pb.UnburyCardsInCurrentDeckIn.ModeValue # pylint: disable=no-member
|
||||
pb.UnburyCardsInCurrentDeckIn.ModeValue # pylint:disable=no-member
|
||||
)
|
||||
BuryOrSuspendModeValue = (
|
||||
pb.BuryOrSuspendCardsIn.ModeValue # pylint:disable=no-member
|
||||
)
|
||||
|
||||
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
|
||||
|
@ -1387,34 +1392,20 @@ where id = ?
|
|||
) -> None:
|
||||
self.col.backend.unbury_cards_in_current_deck(mode)
|
||||
|
||||
def suspendCards(self, ids: List[int]) -> None:
|
||||
"Suspend cards."
|
||||
self.col.log(ids)
|
||||
self.col.db.execute(
|
||||
f"update cards set queue={QUEUE_TYPE_SUSPENDED},mod=?,usn=? where id in "
|
||||
+ ids2str(ids),
|
||||
intTime(),
|
||||
self.col.usn(),
|
||||
def suspend_cards(self, ids: Sequence[int]) -> None:
|
||||
self.col.backend.bury_or_suspend_cards(
|
||||
card_ids=ids, mode=BuryOrSuspendMode.SUSPEND
|
||||
)
|
||||
|
||||
def buryCards(self, cids: List[int], manual: bool = True) -> None:
|
||||
queue = manual and QUEUE_TYPE_MANUALLY_BURIED or QUEUE_TYPE_SIBLING_BURIED
|
||||
self.col.log(cids)
|
||||
self.col.db.execute(
|
||||
"""
|
||||
update cards set queue=?,mod=?,usn=? where id in """
|
||||
+ ids2str(cids),
|
||||
queue,
|
||||
intTime(),
|
||||
self.col.usn(),
|
||||
)
|
||||
def bury_cards(self, ids: Sequence[int], manual: bool = True) -> None:
|
||||
if manual:
|
||||
mode = BuryOrSuspendMode.BURY_USER
|
||||
else:
|
||||
mode = BuryOrSuspendMode.BURY_SCHED
|
||||
self.col.backend.bury_or_suspend_cards(card_ids=ids, mode=mode)
|
||||
|
||||
def buryNote(self, nid: int) -> None:
|
||||
"Bury all cards for note until next session."
|
||||
cids = self.col.db.list(
|
||||
f"select id from cards where nid = ? and queue >= {QUEUE_TYPE_NEW}", nid
|
||||
)
|
||||
self.buryCards(cids)
|
||||
def bury_note(self, note: Note):
|
||||
self.bury_cards(note.card_ids())
|
||||
|
||||
# legacy
|
||||
|
||||
|
@ -1424,7 +1415,14 @@ update cards set queue=?,mod=?,usn=? where id in """
|
|||
)
|
||||
self.unbury_cards_in_current_deck()
|
||||
|
||||
def buryNote(self, nid: int) -> None:
|
||||
note = self.col.getNote(nid)
|
||||
self.bury_cards(note.card_ids())
|
||||
|
||||
def unburyCardsForDeck(self, type: str = "all") -> None:
|
||||
print(
|
||||
"please use unbury_cards_in_current_deck() instead of unburyCardsForDeck()"
|
||||
)
|
||||
if type == "all":
|
||||
mode = UnburyCurrentDeckMode.ALL
|
||||
elif type == "manual":
|
||||
|
@ -1434,6 +1432,8 @@ update cards set queue=?,mod=?,usn=? where id in """
|
|||
self.unbury_cards_in_current_deck(mode)
|
||||
|
||||
unsuspendCards = unsuspend_cards
|
||||
buryCards = bury_cards
|
||||
suspendCards = suspend_cards
|
||||
|
||||
# Sibling spacing
|
||||
##########################################################################
|
||||
|
@ -1469,7 +1469,7 @@ and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""",
|
|||
pass
|
||||
# then bury
|
||||
if toBury:
|
||||
self.buryCards(toBury, manual=False)
|
||||
self.bury_cards(toBury, manual=False)
|
||||
|
||||
# Resetting
|
||||
##########################################################################
|
||||
|
|
|
@ -500,7 +500,7 @@ def test_misc():
|
|||
col.addNote(note)
|
||||
c = note.cards()[0]
|
||||
# burying
|
||||
col.sched.buryNote(c.nid)
|
||||
col.sched.bury_note(note)
|
||||
col.reset()
|
||||
assert not col.sched.getCard()
|
||||
col.sched.unbury_cards_in_current_deck()
|
||||
|
@ -517,11 +517,11 @@ def test_suspend():
|
|||
# suspending
|
||||
col.reset()
|
||||
assert col.sched.getCard()
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
col.reset()
|
||||
assert not col.sched.getCard()
|
||||
# unsuspending
|
||||
col.sched.unsuspendCards([c.id])
|
||||
col.sched.unsuspend_cards([c.id])
|
||||
col.reset()
|
||||
assert col.sched.getCard()
|
||||
# should cope with rev cards being relearnt
|
||||
|
@ -536,8 +536,8 @@ def test_suspend():
|
|||
assert c.due >= time.time()
|
||||
assert c.queue == QUEUE_TYPE_LRN
|
||||
assert c.type == CARD_TYPE_REV
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.unsuspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
col.sched.unsuspend_cards([c.id])
|
||||
c.load()
|
||||
assert c.queue == QUEUE_TYPE_REV
|
||||
assert c.type == CARD_TYPE_REV
|
||||
|
@ -550,7 +550,7 @@ def test_suspend():
|
|||
c.load()
|
||||
assert c.due != 1
|
||||
assert c.did != 1
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
c.load()
|
||||
assert c.due == 1
|
||||
assert c.did == 1
|
||||
|
|
|
@ -600,10 +600,12 @@ def test_bury():
|
|||
col.addNote(note)
|
||||
c2 = note.cards()[0]
|
||||
# burying
|
||||
col.sched.buryCards([c.id], manual=True) # pylint: disable=unexpected-keyword-arg
|
||||
col.sched.bury_cards([c.id], manual=True) # pylint: disable=unexpected-keyword-arg
|
||||
c.load()
|
||||
assert c.queue == QUEUE_TYPE_MANUALLY_BURIED
|
||||
col.sched.buryCards([c2.id], manual=False) # pylint: disable=unexpected-keyword-arg
|
||||
col.sched.bury_cards(
|
||||
[c2.id], manual=False
|
||||
) # pylint: disable=unexpected-keyword-arg
|
||||
c2.load()
|
||||
assert c2.queue == QUEUE_TYPE_SIBLING_BURIED
|
||||
|
||||
|
@ -620,7 +622,7 @@ def test_bury():
|
|||
c2.load()
|
||||
assert c2.queue == QUEUE_TYPE_NEW
|
||||
|
||||
col.sched.buryCards([c.id, c2.id])
|
||||
col.sched.bury_cards([c.id, c2.id])
|
||||
col.sched.unbury_cards_in_current_deck()
|
||||
|
||||
col.reset()
|
||||
|
@ -637,11 +639,11 @@ def test_suspend():
|
|||
# suspending
|
||||
col.reset()
|
||||
assert col.sched.getCard()
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
col.reset()
|
||||
assert not col.sched.getCard()
|
||||
# unsuspending
|
||||
col.sched.unsuspendCards([c.id])
|
||||
col.sched.unsuspend_cards([c.id])
|
||||
col.reset()
|
||||
assert col.sched.getCard()
|
||||
# should cope with rev cards being relearnt
|
||||
|
@ -657,8 +659,8 @@ def test_suspend():
|
|||
due = c.due
|
||||
assert c.queue == QUEUE_TYPE_LRN
|
||||
assert c.type == CARD_TYPE_RELEARNING
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.unsuspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
col.sched.unsuspend_cards([c.id])
|
||||
c.load()
|
||||
assert c.queue == QUEUE_TYPE_LRN
|
||||
assert c.type == CARD_TYPE_RELEARNING
|
||||
|
@ -671,7 +673,7 @@ def test_suspend():
|
|||
c.load()
|
||||
assert c.due != 1
|
||||
assert c.did != 1
|
||||
col.sched.suspendCards([c.id])
|
||||
col.sched.suspend_cards([c.id])
|
||||
c.load()
|
||||
assert c.due != 1
|
||||
assert c.did != 1
|
||||
|
@ -1199,7 +1201,7 @@ def test_moveVersions():
|
|||
col.reset()
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 1)
|
||||
col.sched.buryCards([c.id])
|
||||
col.sched.bury_cards([c.id])
|
||||
c.load()
|
||||
assert c.queue == QUEUE_TYPE_MANUALLY_BURIED
|
||||
|
||||
|
|
|
@ -1672,9 +1672,9 @@ update cards set usn=?, mod=?, did=? where id in """
|
|||
sus = not self.isSuspended()
|
||||
c = self.selectedCards()
|
||||
if sus:
|
||||
self.col.sched.suspendCards(c)
|
||||
self.col.sched.suspend_cards(c)
|
||||
else:
|
||||
self.col.sched.unsuspendCards(c)
|
||||
self.col.sched.unsuspend_cards(c)
|
||||
self.model.reset()
|
||||
self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self)
|
||||
|
||||
|
|
|
@ -794,13 +794,13 @@ time = %(time)d;
|
|||
|
||||
def onSuspend(self) -> None:
|
||||
self.mw.checkpoint(_("Suspend"))
|
||||
self.mw.col.sched.suspendCards([c.id for c in self.card.note().cards()])
|
||||
self.mw.col.sched.suspend_cards([c.id for c in self.card.note().cards()])
|
||||
tooltip(_("Note suspended."))
|
||||
self.mw.reset()
|
||||
|
||||
def onSuspendCard(self) -> None:
|
||||
self.mw.checkpoint(_("Suspend"))
|
||||
self.mw.col.sched.suspendCards([self.card.id])
|
||||
self.mw.col.sched.suspend_cards([self.card.id])
|
||||
tooltip(_("Card suspended."))
|
||||
self.mw.reset()
|
||||
|
||||
|
@ -822,13 +822,13 @@ time = %(time)d;
|
|||
|
||||
def onBuryCard(self) -> None:
|
||||
self.mw.checkpoint(_("Bury"))
|
||||
self.mw.col.sched.buryCards([self.card.id])
|
||||
self.mw.col.sched.bury_cards([self.card.id])
|
||||
self.mw.reset()
|
||||
tooltip(_("Card buried."))
|
||||
|
||||
def onBuryNote(self) -> None:
|
||||
self.mw.checkpoint(_("Bury"))
|
||||
self.mw.col.sched.buryNote(self.card.nid)
|
||||
self.mw.col.sched.bury_note(self.card.note())
|
||||
self.mw.reset()
|
||||
tooltip(_("Note buried."))
|
||||
|
||||
|
|
|
@ -531,6 +531,14 @@ impl BackendService for Backend {
|
|||
})
|
||||
}
|
||||
|
||||
fn bury_or_suspend_cards(&mut self, input: pb::BuryOrSuspendCardsIn) -> BackendResult<Empty> {
|
||||
self.with_col(|col| {
|
||||
let mode = input.mode();
|
||||
let cids: Vec<_> = input.card_ids.into_iter().map(CardID).collect();
|
||||
col.bury_or_suspend_cards(&cids, mode).map(Into::into)
|
||||
})
|
||||
}
|
||||
|
||||
// statistics
|
||||
//-----------------------------------------------
|
||||
|
||||
|
@ -880,6 +888,16 @@ impl BackendService for Backend {
|
|||
})
|
||||
}
|
||||
|
||||
fn cards_of_note(&mut self, input: pb::NoteId) -> BackendResult<pb::CardIDs> {
|
||||
self.with_col(|col| {
|
||||
col.storage
|
||||
.all_card_ids_of_note(NoteID(input.nid))
|
||||
.map(|v| pb::CardIDs {
|
||||
cids: v.into_iter().map(Into::into).collect(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// notetypes
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ use crate::define_newtype;
|
|||
use crate::err::{AnkiError, Result};
|
||||
use crate::notes::NoteID;
|
||||
use crate::{
|
||||
collection::Collection, config::SchedulerVersion, timestamp::TimestampSecs, types::Usn,
|
||||
undo::Undoable,
|
||||
collection::Collection, config::SchedulerVersion, deckconf::INITIAL_EASE_FACTOR,
|
||||
timestamp::TimestampSecs, types::Usn, undo::Undoable,
|
||||
};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
@ -104,11 +104,10 @@ impl Card {
|
|||
|
||||
pub(crate) fn return_home(&mut self, sched: SchedulerVersion) {
|
||||
if self.odid.0 == 0 {
|
||||
// this should not happen
|
||||
// not in a filtered deck
|
||||
return;
|
||||
}
|
||||
|
||||
// fixme: avoid bumping mtime?
|
||||
self.did = self.odid;
|
||||
self.odid.0 = 0;
|
||||
if self.odue > 0 {
|
||||
|
@ -154,6 +153,32 @@ impl Card {
|
|||
self.ctype = CardType::New;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the card from the (re)learning queue.
|
||||
/// This will reset cards in learning.
|
||||
/// Only used in the V1 scheduler.
|
||||
/// Unlike the legacy Python code, this sets the due# to 0 instead of
|
||||
/// one past the previous max due number.
|
||||
pub(crate) fn remove_from_learning(&mut self) {
|
||||
if !matches!(self.queue, CardQueue::Learn | CardQueue::DayLearn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ctype == CardType::Review {
|
||||
// reviews are removed from relearning
|
||||
self.due = self.odue;
|
||||
self.odue = 0;
|
||||
self.queue = CardQueue::Review;
|
||||
} else {
|
||||
// other cards are reset to new
|
||||
self.ctype = CardType::New;
|
||||
self.queue = CardQueue::New;
|
||||
self.ivl = 0;
|
||||
self.due = 0;
|
||||
self.odue = 0;
|
||||
self.factor = INITIAL_EASE_FACTOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UpdateCardUndo(Card);
|
||||
|
|
|
@ -14,6 +14,7 @@ pub use crate::backend_proto::{
|
|||
DeckConfigInner,
|
||||
};
|
||||
pub use schema11::{DeckConfSchema11, NewCardOrderSchema11};
|
||||
pub const INITIAL_EASE_FACTOR: u16 = 2500;
|
||||
|
||||
mod schema11;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use super::{DeckConf, DeckConfID};
|
||||
use super::{DeckConf, DeckConfID, INITIAL_EASE_FACTOR};
|
||||
use crate::backend_proto::deck_config_inner::NewCardOrder;
|
||||
use crate::backend_proto::DeckConfigInner;
|
||||
use crate::{serde::default_on_invalid, timestamp::TimestampSecs, types::Usn};
|
||||
|
@ -153,7 +153,7 @@ impl Default for NewConfSchema11 {
|
|||
NewConfSchema11 {
|
||||
bury: false,
|
||||
delays: vec![1.0, 10.0],
|
||||
initial_factor: 2500,
|
||||
initial_factor: INITIAL_EASE_FACTOR,
|
||||
ints: NewCardIntervals::default(),
|
||||
order: NewCardOrderSchema11::default(),
|
||||
per_day: 20,
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
backend_proto as pb,
|
||||
card::{Card, CardID, CardQueue, CardType},
|
||||
collection::Collection,
|
||||
config::SchedulerVersion,
|
||||
err::Result,
|
||||
};
|
||||
|
||||
|
@ -93,6 +94,54 @@ impl Collection {
|
|||
col.unsuspend_or_unbury_searched_cards()
|
||||
})
|
||||
}
|
||||
|
||||
/// Bury/suspend cards in search table, and clear it.
|
||||
/// Marks the cards as modified.
|
||||
fn bury_or_suspend_searched_cards(
|
||||
&mut self,
|
||||
mode: pb::bury_or_suspend_cards_in::Mode,
|
||||
) -> Result<()> {
|
||||
use pb::bury_or_suspend_cards_in::Mode;
|
||||
let usn = self.usn()?;
|
||||
let sched = self.sched_ver();
|
||||
|
||||
for original in self.storage.all_searched_cards()? {
|
||||
let mut card = original.clone();
|
||||
let desired_queue = match mode {
|
||||
Mode::Suspend => CardQueue::Suspended,
|
||||
Mode::BurySched => CardQueue::SchedBuried,
|
||||
Mode::BuryUser => {
|
||||
if sched == SchedulerVersion::V1 {
|
||||
// v1 scheduler only had one bury type
|
||||
CardQueue::SchedBuried
|
||||
} else {
|
||||
CardQueue::UserBuried
|
||||
}
|
||||
}
|
||||
};
|
||||
if card.queue != desired_queue {
|
||||
if sched == SchedulerVersion::V1 {
|
||||
card.return_home(sched);
|
||||
card.remove_from_learning();
|
||||
}
|
||||
card.queue = desired_queue;
|
||||
self.update_card(&mut card, &original, usn)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.clear_searched_cards()
|
||||
}
|
||||
|
||||
pub fn bury_or_suspend_cards(
|
||||
&mut self,
|
||||
cids: &[CardID],
|
||||
mode: pb::bury_or_suspend_cards_in::Mode,
|
||||
) -> Result<()> {
|
||||
self.transact(None, |col| {
|
||||
col.set_search_table_to_card_ids(cids)?;
|
||||
col.bury_or_suspend_searched_cards(mode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -237,6 +237,13 @@ impl super::SqliteStorage {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn all_card_ids_of_note(&self, nid: NoteID) -> Result<Vec<CardID>> {
|
||||
self.db
|
||||
.prepare_cached("select id from cards where nid = ? order by ord")?
|
||||
.query_and_then(&[nid], |r| Ok(CardID(r.get(0)?)))?
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn note_ids_of_cards(&self, cids: &[CardID]) -> Result<HashSet<NoteID>> {
|
||||
let mut stmt = self
|
||||
.db
|
||||
|
|
Loading…
Reference in a new issue