mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -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 CongratsInfo (Empty) returns (CongratsInfoOut);
|
||||||
rpc RestoreBuriedAndSuspendedCards (CardIDs) returns (Empty);
|
rpc RestoreBuriedAndSuspendedCards (CardIDs) returns (Empty);
|
||||||
rpc UnburyCardsInCurrentDeck (UnburyCardsInCurrentDeckIn) returns (Empty);
|
rpc UnburyCardsInCurrentDeck (UnburyCardsInCurrentDeckIn) returns (Empty);
|
||||||
|
rpc BuryOrSuspendCards (BuryOrSuspendCardsIn) returns (Empty);
|
||||||
|
|
||||||
// stats
|
// stats
|
||||||
|
|
||||||
|
@ -156,6 +157,7 @@ service BackendService {
|
||||||
rpc AfterNoteUpdates (AfterNoteUpdatesIn) returns (Empty);
|
rpc AfterNoteUpdates (AfterNoteUpdatesIn) returns (Empty);
|
||||||
rpc FieldNamesForNotes (FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
|
rpc FieldNamesForNotes (FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
|
||||||
rpc NoteIsDuplicateOrEmpty (Note) returns (NoteIsDuplicateOrEmptyOut);
|
rpc NoteIsDuplicateOrEmpty (Note) returns (NoteIsDuplicateOrEmptyOut);
|
||||||
|
rpc CardsOfNote (NoteID) returns (CardIDs);
|
||||||
|
|
||||||
// note types
|
// note types
|
||||||
|
|
||||||
|
@ -1042,3 +1044,13 @@ message UnburyCardsInCurrentDeckIn {
|
||||||
}
|
}
|
||||||
Mode mode = 1;
|
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)
|
hooks.notes_will_be_deleted(self, nids)
|
||||||
self.backend.remove_notes(note_ids=[], card_ids=card_ids)
|
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
|
# legacy
|
||||||
|
|
||||||
def addNote(self, note: Note) -> int:
|
def addNote(self, note: Note) -> int:
|
||||||
|
|
|
@ -76,12 +76,10 @@ class Note:
|
||||||
return joinFields(self.fields)
|
return joinFields(self.fields)
|
||||||
|
|
||||||
def cards(self) -> List[anki.cards.Card]:
|
def cards(self) -> List[anki.cards.Card]:
|
||||||
return [
|
return [self.col.getCard(id) for id in self.card_ids()]
|
||||||
self.col.getCard(id)
|
|
||||||
for id in self.col.db.list(
|
def card_ids(self) -> Sequence[int]:
|
||||||
"select id from cards where nid = ? order by ord", self.id
|
return self.col.card_ids_of_note(self.id)
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
def model(self) -> Optional[NoteType]:
|
def model(self) -> Optional[NoteType]:
|
||||||
return self.col.models.get(self.mid)
|
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
|
return self._graduatingIvl(card, conf, False, adj=False) * 86400
|
||||||
else:
|
else:
|
||||||
return self._delayForGrade(conf, left)
|
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.consts import *
|
||||||
from anki.decks import Deck, DeckConfig, DeckManager, FilteredDeck, QueueConfig
|
from anki.decks import Deck, DeckConfig, DeckManager, FilteredDeck, QueueConfig
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
|
from anki.notes import Note
|
||||||
from anki.rsbackend import (
|
from anki.rsbackend import (
|
||||||
CountsForDeckToday,
|
CountsForDeckToday,
|
||||||
DeckTreeNode,
|
DeckTreeNode,
|
||||||
|
@ -37,10 +38,14 @@ from anki.rsbackend import (
|
||||||
from anki.utils import ids2str, intTime
|
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:
|
if TYPE_CHECKING:
|
||||||
UnburyCurrentDeckModeValue = (
|
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
|
# card types: 0=new, 1=lrn, 2=rev, 3=relrn
|
||||||
# queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn,
|
# queue types: 0=new, 1=(re)lrn, 2=rev, 3=day (re)lrn,
|
||||||
|
@ -1387,34 +1392,20 @@ where id = ?
|
||||||
) -> None:
|
) -> None:
|
||||||
self.col.backend.unbury_cards_in_current_deck(mode)
|
self.col.backend.unbury_cards_in_current_deck(mode)
|
||||||
|
|
||||||
def suspendCards(self, ids: List[int]) -> None:
|
def suspend_cards(self, ids: Sequence[int]) -> None:
|
||||||
"Suspend cards."
|
self.col.backend.bury_or_suspend_cards(
|
||||||
self.col.log(ids)
|
card_ids=ids, mode=BuryOrSuspendMode.SUSPEND
|
||||||
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 = True) -> None:
|
def bury_cards(self, ids: Sequence[int], manual: bool = True) -> None:
|
||||||
queue = manual and QUEUE_TYPE_MANUALLY_BURIED or QUEUE_TYPE_SIBLING_BURIED
|
if manual:
|
||||||
self.col.log(cids)
|
mode = BuryOrSuspendMode.BURY_USER
|
||||||
self.col.db.execute(
|
else:
|
||||||
"""
|
mode = BuryOrSuspendMode.BURY_SCHED
|
||||||
update cards set queue=?,mod=?,usn=? where id in """
|
self.col.backend.bury_or_suspend_cards(card_ids=ids, mode=mode)
|
||||||
+ ids2str(cids),
|
|
||||||
queue,
|
|
||||||
intTime(),
|
|
||||||
self.col.usn(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def buryNote(self, nid: int) -> None:
|
def bury_note(self, note: Note):
|
||||||
"Bury all cards for note until next session."
|
self.bury_cards(note.card_ids())
|
||||||
cids = self.col.db.list(
|
|
||||||
f"select id from cards where nid = ? and queue >= {QUEUE_TYPE_NEW}", nid
|
|
||||||
)
|
|
||||||
self.buryCards(cids)
|
|
||||||
|
|
||||||
# legacy
|
# legacy
|
||||||
|
|
||||||
|
@ -1424,7 +1415,14 @@ update cards set queue=?,mod=?,usn=? where id in """
|
||||||
)
|
)
|
||||||
self.unbury_cards_in_current_deck()
|
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:
|
def unburyCardsForDeck(self, type: str = "all") -> None:
|
||||||
|
print(
|
||||||
|
"please use unbury_cards_in_current_deck() instead of unburyCardsForDeck()"
|
||||||
|
)
|
||||||
if type == "all":
|
if type == "all":
|
||||||
mode = UnburyCurrentDeckMode.ALL
|
mode = UnburyCurrentDeckMode.ALL
|
||||||
elif type == "manual":
|
elif type == "manual":
|
||||||
|
@ -1434,6 +1432,8 @@ update cards set queue=?,mod=?,usn=? where id in """
|
||||||
self.unbury_cards_in_current_deck(mode)
|
self.unbury_cards_in_current_deck(mode)
|
||||||
|
|
||||||
unsuspendCards = unsuspend_cards
|
unsuspendCards = unsuspend_cards
|
||||||
|
buryCards = bury_cards
|
||||||
|
suspendCards = suspend_cards
|
||||||
|
|
||||||
# Sibling spacing
|
# Sibling spacing
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -1469,7 +1469,7 @@ and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""",
|
||||||
pass
|
pass
|
||||||
# then bury
|
# then bury
|
||||||
if toBury:
|
if toBury:
|
||||||
self.buryCards(toBury, manual=False)
|
self.bury_cards(toBury, manual=False)
|
||||||
|
|
||||||
# Resetting
|
# Resetting
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -500,7 +500,7 @@ def test_misc():
|
||||||
col.addNote(note)
|
col.addNote(note)
|
||||||
c = note.cards()[0]
|
c = note.cards()[0]
|
||||||
# burying
|
# burying
|
||||||
col.sched.buryNote(c.nid)
|
col.sched.bury_note(note)
|
||||||
col.reset()
|
col.reset()
|
||||||
assert not col.sched.getCard()
|
assert not col.sched.getCard()
|
||||||
col.sched.unbury_cards_in_current_deck()
|
col.sched.unbury_cards_in_current_deck()
|
||||||
|
@ -517,11 +517,11 @@ def test_suspend():
|
||||||
# suspending
|
# suspending
|
||||||
col.reset()
|
col.reset()
|
||||||
assert col.sched.getCard()
|
assert col.sched.getCard()
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
col.reset()
|
col.reset()
|
||||||
assert not col.sched.getCard()
|
assert not col.sched.getCard()
|
||||||
# unsuspending
|
# unsuspending
|
||||||
col.sched.unsuspendCards([c.id])
|
col.sched.unsuspend_cards([c.id])
|
||||||
col.reset()
|
col.reset()
|
||||||
assert col.sched.getCard()
|
assert col.sched.getCard()
|
||||||
# should cope with rev cards being relearnt
|
# should cope with rev cards being relearnt
|
||||||
|
@ -536,8 +536,8 @@ def test_suspend():
|
||||||
assert c.due >= time.time()
|
assert c.due >= time.time()
|
||||||
assert c.queue == QUEUE_TYPE_LRN
|
assert c.queue == QUEUE_TYPE_LRN
|
||||||
assert c.type == CARD_TYPE_REV
|
assert c.type == CARD_TYPE_REV
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
col.sched.unsuspendCards([c.id])
|
col.sched.unsuspend_cards([c.id])
|
||||||
c.load()
|
c.load()
|
||||||
assert c.queue == QUEUE_TYPE_REV
|
assert c.queue == QUEUE_TYPE_REV
|
||||||
assert c.type == CARD_TYPE_REV
|
assert c.type == CARD_TYPE_REV
|
||||||
|
@ -550,7 +550,7 @@ def test_suspend():
|
||||||
c.load()
|
c.load()
|
||||||
assert c.due != 1
|
assert c.due != 1
|
||||||
assert c.did != 1
|
assert c.did != 1
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
c.load()
|
c.load()
|
||||||
assert c.due == 1
|
assert c.due == 1
|
||||||
assert c.did == 1
|
assert c.did == 1
|
||||||
|
|
|
@ -600,10 +600,12 @@ def test_bury():
|
||||||
col.addNote(note)
|
col.addNote(note)
|
||||||
c2 = note.cards()[0]
|
c2 = note.cards()[0]
|
||||||
# burying
|
# 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()
|
c.load()
|
||||||
assert c.queue == QUEUE_TYPE_MANUALLY_BURIED
|
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()
|
c2.load()
|
||||||
assert c2.queue == QUEUE_TYPE_SIBLING_BURIED
|
assert c2.queue == QUEUE_TYPE_SIBLING_BURIED
|
||||||
|
|
||||||
|
@ -620,7 +622,7 @@ def test_bury():
|
||||||
c2.load()
|
c2.load()
|
||||||
assert c2.queue == QUEUE_TYPE_NEW
|
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.sched.unbury_cards_in_current_deck()
|
||||||
|
|
||||||
col.reset()
|
col.reset()
|
||||||
|
@ -637,11 +639,11 @@ def test_suspend():
|
||||||
# suspending
|
# suspending
|
||||||
col.reset()
|
col.reset()
|
||||||
assert col.sched.getCard()
|
assert col.sched.getCard()
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
col.reset()
|
col.reset()
|
||||||
assert not col.sched.getCard()
|
assert not col.sched.getCard()
|
||||||
# unsuspending
|
# unsuspending
|
||||||
col.sched.unsuspendCards([c.id])
|
col.sched.unsuspend_cards([c.id])
|
||||||
col.reset()
|
col.reset()
|
||||||
assert col.sched.getCard()
|
assert col.sched.getCard()
|
||||||
# should cope with rev cards being relearnt
|
# should cope with rev cards being relearnt
|
||||||
|
@ -657,8 +659,8 @@ def test_suspend():
|
||||||
due = c.due
|
due = c.due
|
||||||
assert c.queue == QUEUE_TYPE_LRN
|
assert c.queue == QUEUE_TYPE_LRN
|
||||||
assert c.type == CARD_TYPE_RELEARNING
|
assert c.type == CARD_TYPE_RELEARNING
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
col.sched.unsuspendCards([c.id])
|
col.sched.unsuspend_cards([c.id])
|
||||||
c.load()
|
c.load()
|
||||||
assert c.queue == QUEUE_TYPE_LRN
|
assert c.queue == QUEUE_TYPE_LRN
|
||||||
assert c.type == CARD_TYPE_RELEARNING
|
assert c.type == CARD_TYPE_RELEARNING
|
||||||
|
@ -671,7 +673,7 @@ def test_suspend():
|
||||||
c.load()
|
c.load()
|
||||||
assert c.due != 1
|
assert c.due != 1
|
||||||
assert c.did != 1
|
assert c.did != 1
|
||||||
col.sched.suspendCards([c.id])
|
col.sched.suspend_cards([c.id])
|
||||||
c.load()
|
c.load()
|
||||||
assert c.due != 1
|
assert c.due != 1
|
||||||
assert c.did != 1
|
assert c.did != 1
|
||||||
|
@ -1199,7 +1201,7 @@ def test_moveVersions():
|
||||||
col.reset()
|
col.reset()
|
||||||
c = col.sched.getCard()
|
c = col.sched.getCard()
|
||||||
col.sched.answerCard(c, 1)
|
col.sched.answerCard(c, 1)
|
||||||
col.sched.buryCards([c.id])
|
col.sched.bury_cards([c.id])
|
||||||
c.load()
|
c.load()
|
||||||
assert c.queue == QUEUE_TYPE_MANUALLY_BURIED
|
assert c.queue == QUEUE_TYPE_MANUALLY_BURIED
|
||||||
|
|
||||||
|
|
|
@ -1672,9 +1672,9 @@ update cards set usn=?, mod=?, did=? where id in """
|
||||||
sus = not self.isSuspended()
|
sus = not self.isSuspended()
|
||||||
c = self.selectedCards()
|
c = self.selectedCards()
|
||||||
if sus:
|
if sus:
|
||||||
self.col.sched.suspendCards(c)
|
self.col.sched.suspend_cards(c)
|
||||||
else:
|
else:
|
||||||
self.col.sched.unsuspendCards(c)
|
self.col.sched.unsuspend_cards(c)
|
||||||
self.model.reset()
|
self.model.reset()
|
||||||
self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self)
|
self.mw.requireReset(reason=ResetReason.BrowserSuspend, context=self)
|
||||||
|
|
||||||
|
|
|
@ -794,13 +794,13 @@ time = %(time)d;
|
||||||
|
|
||||||
def onSuspend(self) -> None:
|
def onSuspend(self) -> None:
|
||||||
self.mw.checkpoint(_("Suspend"))
|
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."))
|
tooltip(_("Note suspended."))
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
|
|
||||||
def onSuspendCard(self) -> None:
|
def onSuspendCard(self) -> None:
|
||||||
self.mw.checkpoint(_("Suspend"))
|
self.mw.checkpoint(_("Suspend"))
|
||||||
self.mw.col.sched.suspendCards([self.card.id])
|
self.mw.col.sched.suspend_cards([self.card.id])
|
||||||
tooltip(_("Card suspended."))
|
tooltip(_("Card suspended."))
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
|
|
||||||
|
@ -822,13 +822,13 @@ time = %(time)d;
|
||||||
|
|
||||||
def onBuryCard(self) -> None:
|
def onBuryCard(self) -> None:
|
||||||
self.mw.checkpoint(_("Bury"))
|
self.mw.checkpoint(_("Bury"))
|
||||||
self.mw.col.sched.buryCards([self.card.id])
|
self.mw.col.sched.bury_cards([self.card.id])
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
tooltip(_("Card buried."))
|
tooltip(_("Card buried."))
|
||||||
|
|
||||||
def onBuryNote(self) -> None:
|
def onBuryNote(self) -> None:
|
||||||
self.mw.checkpoint(_("Bury"))
|
self.mw.checkpoint(_("Bury"))
|
||||||
self.mw.col.sched.buryNote(self.card.nid)
|
self.mw.col.sched.bury_note(self.card.note())
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
tooltip(_("Note buried."))
|
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
|
// 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
|
// notetypes
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::define_newtype;
|
||||||
use crate::err::{AnkiError, Result};
|
use crate::err::{AnkiError, Result};
|
||||||
use crate::notes::NoteID;
|
use crate::notes::NoteID;
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::Collection, config::SchedulerVersion, timestamp::TimestampSecs, types::Usn,
|
collection::Collection, config::SchedulerVersion, deckconf::INITIAL_EASE_FACTOR,
|
||||||
undo::Undoable,
|
timestamp::TimestampSecs, types::Usn, undo::Undoable,
|
||||||
};
|
};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
@ -104,11 +104,10 @@ impl Card {
|
||||||
|
|
||||||
pub(crate) fn return_home(&mut self, sched: SchedulerVersion) {
|
pub(crate) fn return_home(&mut self, sched: SchedulerVersion) {
|
||||||
if self.odid.0 == 0 {
|
if self.odid.0 == 0 {
|
||||||
// this should not happen
|
// not in a filtered deck
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: avoid bumping mtime?
|
|
||||||
self.did = self.odid;
|
self.did = self.odid;
|
||||||
self.odid.0 = 0;
|
self.odid.0 = 0;
|
||||||
if self.odue > 0 {
|
if self.odue > 0 {
|
||||||
|
@ -154,6 +153,32 @@ impl Card {
|
||||||
self.ctype = CardType::New;
|
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)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct UpdateCardUndo(Card);
|
pub(crate) struct UpdateCardUndo(Card);
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub use crate::backend_proto::{
|
||||||
DeckConfigInner,
|
DeckConfigInner,
|
||||||
};
|
};
|
||||||
pub use schema11::{DeckConfSchema11, NewCardOrderSchema11};
|
pub use schema11::{DeckConfSchema11, NewCardOrderSchema11};
|
||||||
|
pub const INITIAL_EASE_FACTOR: u16 = 2500;
|
||||||
|
|
||||||
mod schema11;
|
mod schema11;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// 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::deck_config_inner::NewCardOrder;
|
||||||
use crate::backend_proto::DeckConfigInner;
|
use crate::backend_proto::DeckConfigInner;
|
||||||
use crate::{serde::default_on_invalid, timestamp::TimestampSecs, types::Usn};
|
use crate::{serde::default_on_invalid, timestamp::TimestampSecs, types::Usn};
|
||||||
|
@ -153,7 +153,7 @@ impl Default for NewConfSchema11 {
|
||||||
NewConfSchema11 {
|
NewConfSchema11 {
|
||||||
bury: false,
|
bury: false,
|
||||||
delays: vec![1.0, 10.0],
|
delays: vec![1.0, 10.0],
|
||||||
initial_factor: 2500,
|
initial_factor: INITIAL_EASE_FACTOR,
|
||||||
ints: NewCardIntervals::default(),
|
ints: NewCardIntervals::default(),
|
||||||
order: NewCardOrderSchema11::default(),
|
order: NewCardOrderSchema11::default(),
|
||||||
per_day: 20,
|
per_day: 20,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
backend_proto as pb,
|
backend_proto as pb,
|
||||||
card::{Card, CardID, CardQueue, CardType},
|
card::{Card, CardID, CardQueue, CardType},
|
||||||
collection::Collection,
|
collection::Collection,
|
||||||
|
config::SchedulerVersion,
|
||||||
err::Result,
|
err::Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,6 +94,54 @@ impl Collection {
|
||||||
col.unsuspend_or_unbury_searched_cards()
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -237,6 +237,13 @@ impl super::SqliteStorage {
|
||||||
.collect()
|
.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>> {
|
pub(crate) fn note_ids_of_cards(&self, cids: &[CardID]) -> Result<HashSet<NoteID>> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
|
|
Loading…
Reference in a new issue