use perform_op() for deck creation
This commit is contained in:
parent
0123a382ec
commit
01161c8ed2
@ -17,6 +17,7 @@ Preferences = _pb.Preferences
|
|||||||
UndoStatus = _pb.UndoStatus
|
UndoStatus = _pb.UndoStatus
|
||||||
OpChanges = _pb.OpChanges
|
OpChanges = _pb.OpChanges
|
||||||
OpChangesWithCount = _pb.OpChangesWithCount
|
OpChangesWithCount = _pb.OpChangesWithCount
|
||||||
|
OpChangesWithID = _pb.OpChangesWithID
|
||||||
DefaultsForAdding = _pb.DeckAndNotetype
|
DefaultsForAdding = _pb.DeckAndNotetype
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
@ -11,7 +11,7 @@ from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
|
|||||||
|
|
||||||
import anki # pylint: disable=unused-import
|
import anki # pylint: disable=unused-import
|
||||||
import anki._backend.backend_pb2 as _pb
|
import anki._backend.backend_pb2 as _pb
|
||||||
from anki.collection import OpChanges, OpChangesWithCount
|
from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithID
|
||||||
from anki.consts import *
|
from anki.consts import *
|
||||||
from anki.errors import NotFoundError
|
from anki.errors import NotFoundError
|
||||||
from anki.utils import from_json_bytes, ids2str, intTime, legacy_func, to_json_bytes
|
from anki.utils import from_json_bytes, ids2str, intTime, legacy_func, to_json_bytes
|
||||||
@ -112,6 +112,20 @@ class DeckManager:
|
|||||||
# Deck save/load
|
# Deck save/load
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
|
def add_normal_deck_with_name(self, name: str) -> OpChangesWithID:
|
||||||
|
"If deck exists, return existing id."
|
||||||
|
if id := self.col.decks.id_for_name(name):
|
||||||
|
return OpChangesWithID(id=id)
|
||||||
|
else:
|
||||||
|
deck = self.col.decks.new_deck_legacy(filtered=False)
|
||||||
|
deck["name"] = name
|
||||||
|
return self.add_deck_legacy(deck)
|
||||||
|
|
||||||
|
def add_deck_legacy(self, deck: Deck) -> OpChangesWithID:
|
||||||
|
"Add a deck created with new_deck_legacy(). Must have id of 0."
|
||||||
|
assert deck["id"] == 0
|
||||||
|
return self.col._backend.add_deck_legacy(to_json_bytes(deck))
|
||||||
|
|
||||||
def id(
|
def id(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
@ -127,9 +141,8 @@ class DeckManager:
|
|||||||
|
|
||||||
deck = self.new_deck_legacy(bool(type))
|
deck = self.new_deck_legacy(bool(type))
|
||||||
deck["name"] = name
|
deck["name"] = name
|
||||||
self.update(deck, preserve_usn=False)
|
out = self.add_deck_legacy(deck)
|
||||||
|
return out.id
|
||||||
return deck["id"]
|
|
||||||
|
|
||||||
@legacy_func(sub="remove")
|
@legacy_func(sub="remove")
|
||||||
def rem(self, did: int, cardsToo: bool = True, childrenToo: bool = True) -> None:
|
def rem(self, did: int, cardsToo: bool = True, childrenToo: bool = True) -> None:
|
||||||
|
@ -8,7 +8,8 @@ from typing import Callable, Sequence
|
|||||||
from anki.decks import DeckID
|
from anki.decks import DeckID
|
||||||
from anki.lang import TR
|
from anki.lang import TR
|
||||||
from aqt import AnkiQt, QWidget
|
from aqt import AnkiQt, QWidget
|
||||||
from aqt.utils import tooltip, tr
|
from aqt.main import PerformOpOptionalSuccessCallback
|
||||||
|
from aqt.utils import getOnlyText, tooltip, tr
|
||||||
|
|
||||||
|
|
||||||
def remove_decks(
|
def remove_decks(
|
||||||
@ -46,3 +47,25 @@ def rename_deck(
|
|||||||
mw.perform_op(
|
mw.perform_op(
|
||||||
lambda: mw.col.decks.rename(deck_id, new_name), after_hooks=after_rename
|
lambda: mw.col.decks.rename(deck_id, new_name), after_hooks=after_rename
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add_deck_dialog(
|
||||||
|
*,
|
||||||
|
mw: AnkiQt,
|
||||||
|
parent: QWidget,
|
||||||
|
default_text: str = "",
|
||||||
|
success: PerformOpOptionalSuccessCallback = None,
|
||||||
|
) -> None:
|
||||||
|
if name := getOnlyText(
|
||||||
|
tr(TR.DECKS_NEW_DECK_NAME), default=default_text, parent=parent
|
||||||
|
).strip():
|
||||||
|
add_deck(mw=mw, name=name, success=success)
|
||||||
|
|
||||||
|
|
||||||
|
def add_deck(
|
||||||
|
*, mw: AnkiQt, name: str, success: PerformOpOptionalSuccessCallback = None
|
||||||
|
) -> None:
|
||||||
|
mw.perform_op(
|
||||||
|
lambda: mw.col.decks.add_normal_deck_with_name(name),
|
||||||
|
success=success,
|
||||||
|
)
|
||||||
|
@ -10,23 +10,13 @@ from typing import Any
|
|||||||
import aqt
|
import aqt
|
||||||
from anki.collection import OpChanges
|
from anki.collection import OpChanges
|
||||||
from anki.decks import DeckTreeNode
|
from anki.decks import DeckTreeNode
|
||||||
from anki.errors import DeckIsFilteredError
|
|
||||||
from anki.utils import intTime
|
from anki.utils import intTime
|
||||||
from aqt import AnkiQt, gui_hooks
|
from aqt import AnkiQt, gui_hooks
|
||||||
from aqt.deck_ops import remove_decks, rename_deck, reparent_decks
|
from aqt.deck_ops import add_deck_dialog, remove_decks, rename_deck, reparent_decks
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
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 (
|
from aqt.utils import TR, askUser, getOnlyText, openLink, shortcut, showInfo, tr
|
||||||
TR,
|
|
||||||
askUser,
|
|
||||||
getOnlyText,
|
|
||||||
openLink,
|
|
||||||
shortcut,
|
|
||||||
showInfo,
|
|
||||||
showWarning,
|
|
||||||
tr,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class DeckBrowserBottomBar:
|
class DeckBrowserBottomBar:
|
||||||
@ -331,15 +321,7 @@ class DeckBrowser:
|
|||||||
openLink(f"{aqt.appShared}decks/")
|
openLink(f"{aqt.appShared}decks/")
|
||||||
|
|
||||||
def _on_create(self) -> None:
|
def _on_create(self) -> None:
|
||||||
deck = getOnlyText(tr(TR.DECKS_NAME_FOR_DECK))
|
add_deck_dialog(mw=self.mw, parent=self.mw)
|
||||||
if deck:
|
|
||||||
try:
|
|
||||||
self.mw.col.decks.id(deck)
|
|
||||||
except DeckIsFilteredError as err:
|
|
||||||
showWarning(str(err))
|
|
||||||
return
|
|
||||||
gui_hooks.sidebar_should_refresh_decks()
|
|
||||||
self.refresh()
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ from anki.collection import (
|
|||||||
Config,
|
Config,
|
||||||
OpChanges,
|
OpChanges,
|
||||||
OpChangesWithCount,
|
OpChangesWithCount,
|
||||||
|
OpChangesWithID,
|
||||||
ReviewUndo,
|
ReviewUndo,
|
||||||
UndoResult,
|
UndoResult,
|
||||||
UndoStatus,
|
UndoStatus,
|
||||||
@ -102,7 +103,8 @@ class HasChangesProperty(Protocol):
|
|||||||
# doesn't actually work for protobuf objects, so new protobuf objects will
|
# doesn't actually work for protobuf objects, so new protobuf objects will
|
||||||
# either need to be added here, or cast at call time
|
# either need to be added here, or cast at call time
|
||||||
ResultWithChanges = TypeVar(
|
ResultWithChanges = TypeVar(
|
||||||
"ResultWithChanges", bound=Union[OpChanges, OpChangesWithCount, HasChangesProperty]
|
"ResultWithChanges",
|
||||||
|
bound=Union[OpChanges, OpChangesWithCount, OpChangesWithID, HasChangesProperty],
|
||||||
)
|
)
|
||||||
|
|
||||||
PerformOpOptionalSuccessCallback = Optional[Callable[[ResultWithChanges], Any]]
|
PerformOpOptionalSuccessCallback = Optional[Callable[[ResultWithChanges], Any]]
|
||||||
|
@ -4,21 +4,20 @@
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import aqt
|
import aqt
|
||||||
from anki.errors import DeckIsFilteredError
|
from anki.collection import OpChangesWithID
|
||||||
from aqt import gui_hooks
|
from aqt import gui_hooks
|
||||||
|
from aqt.deck_ops import add_deck_dialog
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
TR,
|
TR,
|
||||||
HelpPage,
|
HelpPage,
|
||||||
HelpPageArgument,
|
HelpPageArgument,
|
||||||
disable_help_button,
|
disable_help_button,
|
||||||
getOnlyText,
|
|
||||||
openHelp,
|
openHelp,
|
||||||
restoreGeom,
|
restoreGeom,
|
||||||
saveGeom,
|
saveGeom,
|
||||||
shortcut,
|
shortcut,
|
||||||
showInfo,
|
showInfo,
|
||||||
showWarning,
|
|
||||||
tr,
|
tr,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -166,19 +165,14 @@ class StudyDeck(QDialog):
|
|||||||
default = self.form.filter.text()
|
default = self.form.filter.text()
|
||||||
else:
|
else:
|
||||||
default = self.names[self.form.list.currentRow()]
|
default = self.names[self.form.list.currentRow()]
|
||||||
n = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=default)
|
|
||||||
n = n.strip()
|
def success(out: OpChangesWithID) -> None:
|
||||||
if n:
|
deck = self.mw.col.decks.get(out.id)
|
||||||
try:
|
self.name = deck["name"]
|
||||||
did = self.mw.col.decks.id(n)
|
|
||||||
except DeckIsFilteredError as err:
|
|
||||||
showWarning(str(err))
|
|
||||||
return
|
|
||||||
# deck name may not be the same as user input. ex: ", ::
|
|
||||||
self.name = self.mw.col.decks.name(did)
|
|
||||||
# make sure we clean up reset hook when manually exiting
|
# make sure we clean up reset hook when manually exiting
|
||||||
gui_hooks.state_did_reset.remove(self.onReset)
|
gui_hooks.state_did_reset.remove(self.onReset)
|
||||||
if self.mw.state == "deckBrowser":
|
|
||||||
self.mw.deckBrowser.refresh()
|
|
||||||
gui_hooks.sidebar_should_refresh_decks()
|
|
||||||
QDialog.accept(self)
|
QDialog.accept(self)
|
||||||
|
|
||||||
|
add_deck_dialog(mw=self.mw, parent=self, default_text=default, success=success)
|
||||||
|
@ -50,6 +50,11 @@ message OpChangesWithCount {
|
|||||||
OpChanges changes = 2;
|
OpChanges changes = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message OpChangesWithID {
|
||||||
|
int64 id = 1;
|
||||||
|
OpChanges changes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// IDs used in RPC calls
|
// IDs used in RPC calls
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -131,6 +136,7 @@ service SchedulingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
service DecksService {
|
service DecksService {
|
||||||
|
rpc AddDeckLegacy(Json) returns (OpChangesWithID);
|
||||||
rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyIn) returns (DeckID);
|
rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyIn) returns (DeckID);
|
||||||
rpc DeckTree(DeckTreeIn) returns (DeckTreeNode);
|
rpc DeckTree(DeckTreeIn) returns (DeckTreeNode);
|
||||||
rpc DeckTreeLegacy(Empty) returns (Json);
|
rpc DeckTreeLegacy(Empty) returns (Json);
|
||||||
@ -459,7 +465,7 @@ message CardRequirement {
|
|||||||
message Deck {
|
message Deck {
|
||||||
int64 id = 1;
|
int64 id = 1;
|
||||||
string name = 2;
|
string name = 2;
|
||||||
uint32 mtime_secs = 3;
|
int64 mtime_secs = 3;
|
||||||
int32 usn = 4;
|
int32 usn = 4;
|
||||||
DeckCommon common = 5;
|
DeckCommon common = 5;
|
||||||
oneof kind {
|
oneof kind {
|
||||||
|
@ -10,6 +10,15 @@ use crate::{
|
|||||||
pub(super) use pb::decks_service::Service as DecksService;
|
pub(super) use pb::decks_service::Service as DecksService;
|
||||||
|
|
||||||
impl DecksService for Backend {
|
impl DecksService for Backend {
|
||||||
|
fn add_deck_legacy(&self, input: pb::Json) -> Result<pb::OpChangesWithId> {
|
||||||
|
let schema11: DeckSchema11 = serde_json::from_slice(&input.json)?;
|
||||||
|
let mut deck: Deck = schema11.into();
|
||||||
|
self.with_col(|col| {
|
||||||
|
let output = col.add_deck(&mut deck)?;
|
||||||
|
Ok(output.map(|_| deck.id.0).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn add_or_update_deck_legacy(&self, input: pb::AddOrUpdateDeckLegacyIn) -> Result<pb::DeckId> {
|
fn add_or_update_deck_legacy(&self, input: pb::AddOrUpdateDeckLegacyIn) -> Result<pb::DeckId> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
let schema11: DeckSchema11 = serde_json::from_slice(&input.deck)?;
|
let schema11: DeckSchema11 = serde_json::from_slice(&input.deck)?;
|
||||||
@ -148,3 +157,40 @@ impl From<DeckID> for pb::DeckId {
|
|||||||
pb::DeckId { did: did.0 }
|
pb::DeckId { did: did.0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// before we can switch to returning protobuf, we need to make sure we're converting the
|
||||||
|
// deck separators
|
||||||
|
|
||||||
|
// fn new_deck(&self, input: pb::Bool) -> Result<pb::Deck> {
|
||||||
|
// let deck = if input.val {
|
||||||
|
// Deck::new_filtered()
|
||||||
|
// } else {
|
||||||
|
// Deck::new_normal()
|
||||||
|
// };
|
||||||
|
// Ok(deck.into())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl From<pb::Deck> for Deck {
|
||||||
|
// fn from(deck: pb::Deck) -> Self {
|
||||||
|
// Self {
|
||||||
|
// id: deck.id.into(),
|
||||||
|
// name: deck.name,
|
||||||
|
// mtime_secs: deck.mtime_secs.into(),
|
||||||
|
// usn: deck.usn.into(),
|
||||||
|
// common: deck.common.unwrap_or_default(),
|
||||||
|
// kind: deck
|
||||||
|
// .kind
|
||||||
|
// .map(Into::into)
|
||||||
|
// .unwrap_or_else(|| DeckKind::Normal(NormalDeck::default())),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl From<pb::deck::Kind> for DeckKind {
|
||||||
|
// fn from(kind: pb::deck::Kind) -> Self {
|
||||||
|
// match kind {
|
||||||
|
// pb::deck::Kind::Normal(normal) => DeckKind::Normal(normal),
|
||||||
|
// pb::deck::Kind::Filtered(filtered) => DeckKind::Filtered(filtered),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@ -80,12 +80,3 @@ impl From<Vec<String>> for pb::StringList {
|
|||||||
pb::StringList { vals }
|
pb::StringList { vals }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OpOutput<usize>> for pb::OpChangesWithCount {
|
|
||||||
fn from(out: OpOutput<usize>) -> Self {
|
|
||||||
pb::OpChangesWithCount {
|
|
||||||
count: out.output as u32,
|
|
||||||
changes: Some(out.changes.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -44,3 +44,21 @@ impl From<OpOutput<()>> for pb::OpChanges {
|
|||||||
o.changes.into()
|
o.changes.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OpOutput<usize>> for pb::OpChangesWithCount {
|
||||||
|
fn from(out: OpOutput<usize>) -> Self {
|
||||||
|
pb::OpChangesWithCount {
|
||||||
|
count: out.output as u32,
|
||||||
|
changes: Some(out.changes.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OpOutput<i64>> for pb::OpChangesWithId {
|
||||||
|
fn from(out: OpOutput<i64>) -> Self {
|
||||||
|
pb::OpChangesWithId {
|
||||||
|
id: out.output,
|
||||||
|
changes: Some(out.changes.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -207,7 +207,7 @@ impl From<Deck> for DeckProto {
|
|||||||
DeckProto {
|
DeckProto {
|
||||||
id: d.id.0,
|
id: d.id.0,
|
||||||
name: d.name,
|
name: d.name,
|
||||||
mtime_secs: d.mtime_secs.0 as u32,
|
mtime_secs: d.mtime_secs.0,
|
||||||
usn: d.usn.0,
|
usn: d.usn.0,
|
||||||
common: Some(d.common),
|
common: Some(d.common),
|
||||||
kind: Some(d.kind.into()),
|
kind: Some(d.kind.into()),
|
||||||
|
@ -86,3 +86,15 @@ pub struct OpOutput<T> {
|
|||||||
pub output: T,
|
pub output: T,
|
||||||
pub changes: OpChanges,
|
pub changes: OpChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> OpOutput<T> {
|
||||||
|
pub(crate) fn map<F, N>(self, func: F) -> OpOutput<N>
|
||||||
|
where
|
||||||
|
F: FnOnce(T) -> N,
|
||||||
|
{
|
||||||
|
OpOutput {
|
||||||
|
output: func(self.output),
|
||||||
|
changes: self.changes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user