support undo for (renamed) unbury_deck() action

This commit is contained in:
Damien Elmes 2021-04-30 20:03:20 +10:00
parent d0f3007fad
commit abab4826bb
16 changed files with 92 additions and 83 deletions

View file

@ -8,7 +8,7 @@ ignored-classes=
BrowserRow, BrowserRow,
FormatTimespanIn, FormatTimespanIn,
AnswerCardIn, AnswerCardIn,
UnburyCardsInCurrentDeckIn, UnburyDeckIn,
BuryOrSuspendCardsIn, BuryOrSuspendCardsIn,
NoteIsDuplicateOrEmptyOut, NoteIsDuplicateOrEmptyOut,
BackendError, BackendError,

View file

@ -7,7 +7,7 @@ import sys
import anki.scheduler.base as _base import anki.scheduler.base as _base
UnburyCurrentDeck = _base.UnburyCurrentDeck UnburyDeck = _base.UnburyDeck
CongratsInfo = _base.CongratsInfo CongratsInfo = _base.CongratsInfo
BuryOrSuspend = _base.BuryOrSuspend BuryOrSuspend = _base.BuryOrSuspend
FilteredDeckForUpdate = _base.FilteredDeckForUpdate FilteredDeckForUpdate = _base.FilteredDeckForUpdate

View file

@ -20,7 +20,7 @@ from anki.notes import NoteId
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
CongratsInfo = _pb.CongratsInfoOut CongratsInfo = _pb.CongratsInfoOut
UnburyCurrentDeck = _pb.UnburyCardsInCurrentDeckIn UnburyDeck = _pb.UnburyDeckIn
BuryOrSuspend = _pb.BuryOrSuspendCardsIn BuryOrSuspend = _pb.BuryOrSuspendCardsIn
FilteredDeckForUpdate = _pb.FilteredDeckForUpdate FilteredDeckForUpdate = _pb.FilteredDeckForUpdate
@ -118,11 +118,12 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
def unbury_cards(self, ids: List[CardId]) -> OpChanges: def unbury_cards(self, ids: List[CardId]) -> OpChanges:
return self.col._backend.restore_buried_and_suspended_cards(ids) return self.col._backend.restore_buried_and_suspended_cards(ids)
def unbury_cards_in_current_deck( def unbury_deck(
self, self,
mode: UnburyCurrentDeck.Mode.V = UnburyCurrentDeck.ALL, deck_id: DeckId,
) -> None: mode: UnburyDeck.Mode.V = UnburyDeck.ALL,
self.col._backend.unbury_cards_in_current_deck(mode) ) -> OpChanges:
return self.col._backend.unbury_deck(deck_id=deck_id, mode=mode)
def suspend_cards(self, ids: Sequence[CardId]) -> OpChangesWithCount: def suspend_cards(self, ids: Sequence[CardId]) -> OpChangesWithCount:
return self.col._backend.bury_or_suspend_cards( return self.col._backend.bury_or_suspend_cards(

View file

@ -7,7 +7,7 @@ from anki.cards import Card, CardId
from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN
from anki.decks import DeckConfigDict, DeckId from anki.decks import DeckConfigDict, DeckId
from anki.notes import NoteId from anki.notes import NoteId
from anki.scheduler.base import SchedulerBase, UnburyCurrentDeck from anki.scheduler.base import SchedulerBase, UnburyDeck
from anki.utils import from_json_bytes, ids2str from anki.utils import from_json_bytes, ids2str
@ -24,22 +24,18 @@ class SchedulerBaseWithLegacy(SchedulerBase):
self.bury_cards(note.card_ids()) self.bury_cards(note.card_ids())
def unburyCards(self) -> None: def unburyCards(self) -> None:
print( print("please use unbury_cards() or unbury_deck() instead of unburyCards()")
"please use unbury_cards() or unbury_cards_in_current_deck instead of unburyCards()" self.unbury_deck(self.col.decks.get_current_id())
)
self.unbury_cards_in_current_deck()
def unburyCardsForDeck(self, type: str = "all") -> None: def unburyCardsForDeck(self, type: str = "all") -> None:
print( print("please use unbury_deck() instead of unburyCardsForDeck()")
"please use unbury_cards_in_current_deck() instead of unburyCardsForDeck()"
)
if type == "all": if type == "all":
mode = UnburyCurrentDeck.ALL mode = UnburyDeck.ALL
elif type == "manual": elif type == "manual":
mode = UnburyCurrentDeck.USER_ONLY mode = UnburyDeck.USER_ONLY
else: # elif type == "siblings": else: # elif type == "siblings":
mode = UnburyCurrentDeck.SCHED_ONLY mode = UnburyDeck.SCHED_ONLY
self.unbury_cards_in_current_deck(mode) self.unbury_deck(self.col.decks.get_current_id(), mode)
def finishedMsg(self) -> str: def finishedMsg(self) -> str:
print("finishedMsg() is obsolete") print("finishedMsg() is obsolete")

View file

@ -4,13 +4,14 @@
import copy import copy
import time import time
from anki import Collection
from anki.consts import * from anki.consts import *
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.utils import intTime from anki.utils import intTime
from tests.shared import getEmptyCol as getEmptyColOrig from tests.shared import getEmptyCol as getEmptyColOrig
def getEmptyCol(): def getEmptyCol() -> Collection:
col = getEmptyColOrig() col = getEmptyColOrig()
# only safe in test environment # only safe in test environment
col.set_config("schedVer", 1) col.set_config("schedVer", 1)
@ -505,7 +506,7 @@ def test_misc():
col.sched.bury_notes([note.id]) col.sched.bury_notes([note.id])
col.reset() col.reset()
assert not col.sched.getCard() assert not col.sched.getCard()
col.sched.unbury_cards_in_current_deck() col.sched.unbury_deck(deck_id=col.decks.get_current_id())
col.reset() col.reset()
assert col.sched.getCard() assert col.sched.getCard()

View file

@ -11,7 +11,7 @@ import pytest
from anki import hooks from anki import hooks
from anki.consts import * from anki.consts import *
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.scheduler import UnburyCurrentDeck from anki.scheduler import UnburyDeck
from anki.utils import intTime from anki.utils import intTime
from tests.shared import getEmptyCol as getEmptyColOrig from tests.shared import getEmptyCol as getEmptyColOrig
@ -673,18 +673,20 @@ def test_bury():
col.reset() col.reset()
assert not col.sched.getCard() assert not col.sched.getCard()
col.sched.unbury_cards_in_current_deck(UnburyCurrentDeck.USER_ONLY) col.sched.unbury_deck(deck_id=col.decks.get_current_id(), mode=UnburyDeck.USER_ONLY)
c.load() c.load()
assert c.queue == QUEUE_TYPE_NEW assert c.queue == QUEUE_TYPE_NEW
c2.load() c2.load()
assert c2.queue == QUEUE_TYPE_SIBLING_BURIED assert c2.queue == QUEUE_TYPE_SIBLING_BURIED
col.sched.unbury_cards_in_current_deck(UnburyCurrentDeck.SCHED_ONLY) col.sched.unbury_deck(
deck_id=col.decks.get_current_id(), mode=UnburyDeck.SCHED_ONLY
)
c2.load() c2.load()
assert c2.queue == QUEUE_TYPE_NEW assert c2.queue == QUEUE_TYPE_NEW
col.sched.bury_cards([c.id, c2.id]) col.sched.bury_cards([c.id, c2.id])
col.sched.unbury_cards_in_current_deck() col.sched.unbury_deck(deck_id=col.decks.get_current_id())
col.reset() col.reset()

View file

@ -10,7 +10,8 @@ ignored-classes=
BrowserRow, BrowserRow,
SearchNode, SearchNode,
Config, Config,
OpChanges OpChanges,
UnburyDeckIn,
[REPORTS] [REPORTS]
output-format=colorized output-format=colorized

View file

@ -16,7 +16,7 @@ from anki.collection import (
) )
from anki.decks import DeckId from anki.decks import DeckId
from anki.notes import NoteId from anki.notes import NoteId
from anki.scheduler import FilteredDeckForUpdate from anki.scheduler import FilteredDeckForUpdate, UnburyDeck
from aqt.operations import CollectionOp from aqt.operations import CollectionOp
from aqt.qt import * from aqt.qt import *
from aqt.utils import disable_help_button, getText, tooltip, tr from aqt.utils import disable_help_button, getText, tooltip, tr
@ -196,3 +196,14 @@ def add_or_update_filtered_deck(
deck: FilteredDeckForUpdate, deck: FilteredDeckForUpdate,
) -> CollectionOp[OpChangesWithId]: ) -> CollectionOp[OpChangesWithId]:
return CollectionOp(parent, lambda col: col.sched.add_or_update_filtered_deck(deck)) return CollectionOp(parent, lambda col: col.sched.add_or_update_filtered_deck(deck))
def unbury_deck(
*,
parent: QWidget,
deck_id: DeckId,
mode: UnburyDeck.Mode.V = UnburyDeck.ALL,
) -> CollectionOp[OpChanges]:
return CollectionOp(
parent, lambda col: col.sched.unbury_deck(deck_id=deck_id, mode=mode)
)

View file

@ -7,9 +7,14 @@ from typing import Any, Callable, Dict, List, Optional, Tuple
import aqt import aqt
from anki.collection import OpChanges from anki.collection import OpChanges
from anki.scheduler import UnburyDeck
from aqt import gui_hooks from aqt import gui_hooks
from aqt.deckdescription import DeckDescriptionDialog from aqt.deckdescription import DeckDescriptionDialog
from aqt.operations.scheduling import empty_filtered_deck, rebuild_filtered_deck from aqt.operations.scheduling import (
empty_filtered_deck,
rebuild_filtered_deck,
unbury_deck,
)
from aqt.sound import av_player from aqt.sound import av_player
from aqt.toolbar import BottomBar from aqt.toolbar import BottomBar
from aqt.utils import askUserDialog, openLink, shortcut, tooltip, tr from aqt.utils import askUserDialog, openLink, shortcut, tooltip, tr
@ -102,7 +107,7 @@ class Overview:
elif url == "studymore" or url == "customStudy": elif url == "studymore" or url == "customStudy":
self.onStudyMore() self.onStudyMore()
elif url == "unbury": elif url == "unbury":
self.onUnbury() self.on_unbury()
elif url == "description": elif url == "description":
self.edit_description() self.edit_description()
elif url.lower().startswith("http"): elif url.lower().startswith("http"):
@ -115,7 +120,7 @@ class Overview:
("r", self.rebuild_current_filtered_deck), ("r", self.rebuild_current_filtered_deck),
("e", self.empty_current_filtered_deck), ("e", self.empty_current_filtered_deck),
("c", self.onCustomStudyKey), ("c", self.onCustomStudyKey),
("u", self.onUnbury), ("u", self.on_unbury),
] ]
def _current_deck_is_filtered(self) -> int: def _current_deck_is_filtered(self) -> int:
@ -135,34 +140,33 @@ class Overview:
if not self._current_deck_is_filtered(): if not self._current_deck_is_filtered():
self.onStudyMore() self.onStudyMore()
def onUnbury(self) -> None: def on_unbury(self) -> None:
if self.mw.col.schedVer() == 1: mode = UnburyDeck.Mode.ALL
self.mw.col.sched.unburyCardsForDeck() if self.mw.col.schedVer() != 1:
self.mw.reset() info = self.mw.col.sched.congratulations_info()
return if info.have_sched_buried and info.have_user_buried:
opts = [
tr.studying_manually_buried_cards(),
tr.studying_buried_siblings(),
tr.studying_all_buried_cards(),
tr.actions_cancel(),
]
info = self.mw.col.sched.congratulations_info() diag = askUserDialog(tr.studying_what_would_you_like_to_unbury(), opts)
if info.have_sched_buried and info.have_user_buried: diag.setDefault(0)
opts = [ ret = diag.run()
tr.studying_manually_buried_cards(), if ret == opts[0]:
tr.studying_buried_siblings(), mode = UnburyDeck.Mode.USER_ONLY
tr.studying_all_buried_cards(), elif ret == opts[1]:
tr.actions_cancel(), mode = UnburyDeck.Mode.SCHED_ONLY
] elif ret == opts[3]:
return
diag = askUserDialog(tr.studying_what_would_you_like_to_unbury(), opts) unbury_deck(
diag.setDefault(0) parent=self.mw, deck_id=self.mw.col.decks.get_current_id(), mode=mode
ret = diag.run() ).run_in_background()
if ret == opts[0]:
self.mw.col.sched.unburyCardsForDeck(type="manual")
elif ret == opts[1]:
self.mw.col.sched.unburyCardsForDeck(type="siblings")
elif ret == opts[2]:
self.mw.col.sched.unburyCardsForDeck(type="all")
else:
self.mw.col.sched.unburyCardsForDeck(type="all")
self.mw.reset() onUnbury = on_unbury
# HTML # HTML
############################################################ ############################################################

View file

@ -119,7 +119,7 @@ service SchedulingService {
rpc CountsForDeckToday(DeckId) returns (CountsForDeckTodayOut); rpc CountsForDeckToday(DeckId) returns (CountsForDeckTodayOut);
rpc CongratsInfo(Empty) returns (CongratsInfoOut); rpc CongratsInfo(Empty) returns (CongratsInfoOut);
rpc RestoreBuriedAndSuspendedCards(CardIds) returns (OpChanges); rpc RestoreBuriedAndSuspendedCards(CardIds) returns (OpChanges);
rpc UnburyCardsInCurrentDeck(UnburyCardsInCurrentDeckIn) returns (Empty); rpc UnburyDeck(UnburyDeckIn) returns (OpChanges);
rpc BuryOrSuspendCards(BuryOrSuspendCardsIn) returns (OpChangesWithCount); rpc BuryOrSuspendCards(BuryOrSuspendCardsIn) returns (OpChangesWithCount);
rpc EmptyFilteredDeck(DeckId) returns (OpChanges); rpc EmptyFilteredDeck(DeckId) returns (OpChanges);
rpc RebuildFilteredDeck(DeckId) returns (OpChangesWithCount); rpc RebuildFilteredDeck(DeckId) returns (OpChangesWithCount);
@ -1333,13 +1333,14 @@ message CongratsInfoOut {
string deck_description = 9; string deck_description = 9;
} }
message UnburyCardsInCurrentDeckIn { message UnburyDeckIn {
enum Mode { enum Mode {
ALL = 0; ALL = 0;
SCHED_ONLY = 1; SCHED_ONLY = 1;
USER_ONLY = 2; USER_ONLY = 2;
} }
Mode mode = 1; int64 deck_id = 1;
Mode mode = 2;
} }
message BuryOrSuspendCardsIn { message BuryOrSuspendCardsIn {

View file

@ -77,12 +77,9 @@ impl SchedulingService for Backend {
self.with_col(|col| col.unbury_or_unsuspend_cards(&cids).map(Into::into)) self.with_col(|col| col.unbury_or_unsuspend_cards(&cids).map(Into::into))
} }
fn unbury_cards_in_current_deck( fn unbury_deck(&self, input: pb::UnburyDeckIn) -> Result<pb::OpChanges> {
&self,
input: pb::UnburyCardsInCurrentDeckIn,
) -> Result<pb::Empty> {
self.with_col(|col| { self.with_col(|col| {
col.unbury_cards_in_current_deck(input.mode()) col.unbury_deck(input.deck_id.into(), input.mode())
.map(Into::into) .map(Into::into)
}) })
} }

View file

@ -5,9 +5,7 @@ use std::collections::HashMap;
use super::{CardGenContext, Notetype}; use super::{CardGenContext, Notetype};
use crate::{ use crate::{
collection::Collection, prelude::*,
error::Result,
match_all,
search::{Node, SortMode, TemplateKind}, search::{Node, SortMode, TemplateKind},
}; };

View file

@ -12,6 +12,7 @@ pub use crate::{
decks::{Deck, DeckId, DeckKind, NativeDeckName}, decks::{Deck, DeckId, DeckKind, NativeDeckName},
error::{AnkiError, Result}, error::{AnkiError, Result},
i18n::I18n, i18n::I18n,
match_all, match_any,
notes::{Note, NoteId}, notes::{Note, NoteId},
notetype::{Notetype, NotetypeId}, notetype::{Notetype, NotetypeId},
ops::{Op, OpChanges, OpOutput}, ops::{Op, OpChanges, OpOutput},

View file

@ -4,15 +4,12 @@
use super::timing::SchedTimingToday; use super::timing::SchedTimingToday;
use crate::{ use crate::{
backend_proto::{ backend_proto::{
bury_or_suspend_cards_in::Mode as BuryOrSuspendMode, bury_or_suspend_cards_in::Mode as BuryOrSuspendMode, unbury_deck_in::Mode as UnburyDeckMode,
unbury_cards_in_current_deck_in::Mode as UnburyDeckMode,
}, },
card::{Card, CardId, CardQueue}, card::CardQueue,
collection::Collection,
config::SchedulerVersion, config::SchedulerVersion,
error::Result,
prelude::*, prelude::*,
search::SortMode, search::{SortMode, StateKind},
}; };
impl Card { impl Card {
@ -73,14 +70,14 @@ impl Collection {
}) })
} }
pub fn unbury_cards_in_current_deck(&mut self, mode: UnburyDeckMode) -> Result<()> { pub fn unbury_deck(&mut self, deck_id: DeckId, mode: UnburyDeckMode) -> Result<OpOutput<()>> {
let search = match mode { let state = match mode {
UnburyDeckMode::All => "is:buried", UnburyDeckMode::All => StateKind::Buried,
UnburyDeckMode::UserOnly => "is:buried-manually", UnburyDeckMode::UserOnly => StateKind::UserBuried,
UnburyDeckMode::SchedOnly => "is:buried-sibling", UnburyDeckMode::SchedOnly => StateKind::SchedBuried,
}; };
self.transact_no_undo(|col| { self.transact(Op::UnburyUnsuspend, |col| {
col.search_cards_into_table(&format!("deck:current {}", search), SortMode::NoOrder)?; col.search_cards_into_table(match_all![deck_id, state], SortMode::NoOrder)?;
col.unsuspend_or_unbury_searched_cards() col.unsuspend_or_unbury_searched_cards()
}) })
} }

View file

@ -8,9 +8,8 @@ use rand::seq::SliceRandom;
use crate::{ use crate::{
card::{CardQueue, CardType}, card::{CardQueue, CardType},
deckconfig::NewCardOrder, deckconfig::NewCardOrder,
match_all,
prelude::*, prelude::*,
search::{Node, SortMode, StateKind}, search::{SortMode, StateKind},
}; };
impl Card { impl Card {

View file

@ -70,14 +70,14 @@ impl Node {
#[macro_export] #[macro_export]
macro_rules! match_all { macro_rules! match_all {
($($param:expr),+ $(,)?) => { ($($param:expr),+ $(,)?) => {
Node::all(vec![$($param.into()),+]) $crate::search::Node::all(vec![$($param.into()),+])
}; };
} }
#[macro_export] #[macro_export]
macro_rules! match_any { macro_rules! match_any {
($($param:expr),+ $(,)?) => { ($($param:expr),+ $(,)?) => {
Node::any(vec![$($param.into()),+]) $crate::search::Node::any(vec![$($param.into()),+])
}; };
} }