embed deck config and expose to frontend

This commit is contained in:
Damien Elmes 2021-04-04 22:24:35 +10:00
parent 037df9522b
commit 42a4d11416
6 changed files with 103 additions and 74 deletions

View file

@ -39,7 +39,7 @@ from anki.cards import Card, CardId
from anki.config import Config, ConfigManager from anki.config import Config, ConfigManager
from anki.consts import * from anki.consts import *
from anki.dbproxy import DBProxy from anki.dbproxy import DBProxy
from anki.decks import Deck, DeckId, DeckManager from anki.decks import Deck, DeckConfig, DeckConfigId, DeckId, DeckManager
from anki.errors import AbortSchemaModification, DBError from anki.errors import AbortSchemaModification, DBError
from anki.lang import FormatTimeSpan from anki.lang import FormatTimeSpan
from anki.media import MediaManager, media_paths_from_col_path from anki.media import MediaManager, media_paths_from_col_path
@ -335,6 +335,10 @@ class Collection:
"Get a new-style deck object. Currently read-only." "Get a new-style deck object. Currently read-only."
return self._backend.get_deck(id) return self._backend.get_deck(id)
def get_deck_config(self, id: DeckConfigId) -> DeckConfig:
"Get a new-style deck config object. Currently read-only."
return self._backend.get_deck_config(id)
def get_notetype(self, id: NotetypeId) -> Notetype: def get_notetype(self, id: NotetypeId) -> Notetype:
"""Get a new-style notetype object. This is not cached; avoid calling frequently.""" """Get a new-style notetype object. This is not cached; avoid calling frequently."""
return self._backend.get_notetype(id) return self._backend.get_notetype(id)

View file

@ -32,12 +32,13 @@ DeckConfigDict = Dict[str, Any]
# currently only supports read-only access # currently only supports read-only access
Deck = _pb.Deck Deck = _pb.Deck
DeckConfig = _pb.DeckConfig
DeckId = NewType("DeckId", int) DeckId = NewType("DeckId", int)
DeckConfId = NewType("DeckConfId", int) DeckConfigId = NewType("DeckConfigId", int)
DEFAULT_DECK_ID = DeckId(1) DEFAULT_DECK_ID = DeckId(1)
DEFAULT_DECK_CONF_ID = DeckConfId(1) DEFAULT_DECK_CONF_ID = DeckConfigId(1)
class DecksDictProxy: class DecksDictProxy:
@ -128,7 +129,7 @@ class DeckManager:
self, self,
name: str, name: str,
create: bool = True, create: bool = True,
type: DeckConfId = DeckConfId(0), type: DeckConfigId = DeckConfigId(0),
) -> Optional[DeckId]: ) -> Optional[DeckId]:
"Add a deck with NAME. Reuse deck if already exists. Return id as int." "Add a deck with NAME. Reuse deck if already exists. Return id as int."
id = self.id_for_name(name) id = self.id_for_name(name)
@ -323,7 +324,7 @@ class DeckManager:
deck = self.get(did, default=False) deck = self.get(did, default=False)
assert deck assert deck
if "conf" in deck: if "conf" in deck:
dcid = DeckConfId(int(deck["conf"])) # may be a string dcid = DeckConfigId(int(deck["conf"])) # may be a string
conf = self.get_config(dcid) conf = self.get_config(dcid)
if not conf: if not conf:
# fall back on default # fall back on default
@ -333,7 +334,7 @@ class DeckManager:
# dynamic decks have embedded conf # dynamic decks have embedded conf
return deck return deck
def get_config(self, conf_id: DeckConfId) -> Optional[DeckConfigDict]: def get_config(self, conf_id: DeckConfigId) -> Optional[DeckConfigDict]:
try: try:
return from_json_bytes(self.col._backend.get_deck_config_legacy(conf_id)) return from_json_bytes(self.col._backend.get_deck_config_legacy(conf_id))
except NotFoundError: except NotFoundError:
@ -358,10 +359,10 @@ class DeckManager:
def add_config_returning_id( def add_config_returning_id(
self, name: str, clone_from: Optional[DeckConfigDict] = None self, name: str, clone_from: Optional[DeckConfigDict] = None
) -> DeckConfId: ) -> DeckConfigId:
return self.add_config(name, clone_from)["id"] return self.add_config(name, clone_from)["id"]
def remove_config(self, id: DeckConfId) -> None: def remove_config(self, id: DeckConfigId) -> None:
"Remove a configuration and update all decks using it." "Remove a configuration and update all decks using it."
self.col.modSchema(check=True) self.col.modSchema(check=True)
for g in self.all(): for g in self.all():
@ -373,7 +374,7 @@ class DeckManager:
self.save(g) self.save(g)
self.col._backend.remove_deck_config(id) self.col._backend.remove_deck_config(id)
def setConf(self, grp: DeckConfigDict, id: DeckConfId) -> None: def setConf(self, grp: DeckConfigDict, id: DeckConfigId) -> None:
grp["conf"] = id grp["conf"] = id
self.save(grp) self.save(grp)

View file

@ -219,6 +219,7 @@ service CardRenderingService {
service DeckConfigService { service DeckConfigService {
rpc AddOrUpdateDeckConfigLegacy(AddOrUpdateDeckConfigLegacyIn) rpc AddOrUpdateDeckConfigLegacy(AddOrUpdateDeckConfigLegacyIn)
returns (DeckConfigId); returns (DeckConfigId);
rpc GetDeckConfig(DeckConfigId) returns (DeckConfig);
rpc AllDeckConfigLegacy(Empty) returns (Json); rpc AllDeckConfigLegacy(Empty) returns (Json);
rpc GetDeckConfigLegacy(DeckConfigId) returns (Json); rpc GetDeckConfigLegacy(DeckConfigId) returns (Json);
rpc NewDeckConfigLegacy(Empty) returns (Json); rpc NewDeckConfigLegacy(Empty) returns (Json);
@ -294,7 +295,8 @@ service CardsService {
// These should be moved to a separate file in the future // These should be moved to a separate file in the future
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
message DeckConfigInner { message DeckConfig {
message Config {
enum NewCardOrder { enum NewCardOrder {
NEW_CARD_ORDER_DUE = 0; NEW_CARD_ORDER_DUE = 0;
NEW_CARD_ORDER_RANDOM = 1; NEW_CARD_ORDER_RANDOM = 1;
@ -354,10 +356,14 @@ message DeckConfigInner {
bool bury_reviews = 28; bool bury_reviews = 28;
bytes other = 255; bytes other = 255;
} }
// Containers for passing around database objects int64 id = 1;
/////////////////////////////////////////////////////////// string name = 2;
int64 mtime_secs = 3;
int32 usn = 4;
Config config = 5;
}
message Deck { message Deck {
message Common { message Common {
@ -504,6 +510,9 @@ message Notetype {
repeated Template templates = 9; repeated Template templates = 9;
} }
// Database objects
///////////////////////////////////////////////////////////
message Note { message Note {
int64 id = 1; int64 id = 1;
string guid = 2; string guid = 2;

View file

@ -38,6 +38,10 @@ impl DeckConfigService for Backend {
.map(Into::into) .map(Into::into)
} }
fn get_deck_config(&self, input: pb::DeckConfigId) -> Result<pb::DeckConfig> {
self.with_col(|col| Ok(col.get_deck_config(input.into(), true)?.unwrap().into()))
}
fn get_deck_config_legacy(&self, input: pb::DeckConfigId) -> Result<pb::Json> { fn get_deck_config_legacy(&self, input: pb::DeckConfigId) -> Result<pb::Json> {
self.with_col(|col| { self.with_col(|col| {
let conf = col.get_deck_config(input.into(), true)?.unwrap(); let conf = col.get_deck_config(input.into(), true)?.unwrap();
@ -58,3 +62,15 @@ impl DeckConfigService for Backend {
.map(Into::into) .map(Into::into)
} }
} }
impl From<DeckConf> for pb::DeckConfig {
fn from(c: DeckConf) -> Self {
pb::DeckConfig {
id: c.id.0,
name: c.name,
mtime_secs: c.mtime_secs.0,
usn: c.usn.0,
config: Some(c.inner),
}
}
}

View file

@ -11,8 +11,8 @@ use crate::{
}; };
pub use crate::backend_proto::{ pub use crate::backend_proto::{
deck_config_inner::{LeechAction, NewCardOrder, ReviewCardOrder, ReviewMix}, deck_config::config::{LeechAction, NewCardOrder, ReviewCardOrder, ReviewMix},
DeckConfigInner, deck_config::Config as DeckConfigInner,
}; };
pub use schema11::{DeckConfSchema11, NewCardOrderSchema11}; pub use schema11::{DeckConfSchema11, NewCardOrderSchema11};
/// Old deck config and cards table store 250% as 2500. /// Old deck config and cards table store 250% as 2500.

View file

@ -2,8 +2,7 @@
// 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, INITIAL_EASE_FACTOR_THOUSANDS}; use super::{DeckConf, DeckConfId, INITIAL_EASE_FACTOR_THOUSANDS};
use crate::backend_proto::deck_config_inner::NewCardOrder; use super::{DeckConfigInner, NewCardOrder};
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};
use serde_aux::field_attributes::deserialize_number_from_string; use serde_aux::field_attributes::deserialize_number_from_string;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};