diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 4eb2975c0..8eea0dfd8 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -39,7 +39,7 @@ from anki.cards import Card, CardId from anki.config import Config, ConfigManager from anki.consts import * 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.lang import FormatTimeSpan 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." 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: """Get a new-style notetype object. This is not cached; avoid calling frequently.""" return self._backend.get_notetype(id) diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 1328b1529..7ba21c418 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -32,12 +32,13 @@ DeckConfigDict = Dict[str, Any] # currently only supports read-only access Deck = _pb.Deck +DeckConfig = _pb.DeckConfig DeckId = NewType("DeckId", int) -DeckConfId = NewType("DeckConfId", int) +DeckConfigId = NewType("DeckConfigId", int) DEFAULT_DECK_ID = DeckId(1) -DEFAULT_DECK_CONF_ID = DeckConfId(1) +DEFAULT_DECK_CONF_ID = DeckConfigId(1) class DecksDictProxy: @@ -128,7 +129,7 @@ class DeckManager: self, name: str, create: bool = True, - type: DeckConfId = DeckConfId(0), + type: DeckConfigId = DeckConfigId(0), ) -> Optional[DeckId]: "Add a deck with NAME. Reuse deck if already exists. Return id as int." id = self.id_for_name(name) @@ -323,7 +324,7 @@ class DeckManager: deck = self.get(did, default=False) assert 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) if not conf: # fall back on default @@ -333,7 +334,7 @@ class DeckManager: # dynamic decks have embedded conf return deck - def get_config(self, conf_id: DeckConfId) -> Optional[DeckConfigDict]: + def get_config(self, conf_id: DeckConfigId) -> Optional[DeckConfigDict]: try: return from_json_bytes(self.col._backend.get_deck_config_legacy(conf_id)) except NotFoundError: @@ -358,10 +359,10 @@ class DeckManager: def add_config_returning_id( self, name: str, clone_from: Optional[DeckConfigDict] = None - ) -> DeckConfId: + ) -> DeckConfigId: 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." self.col.modSchema(check=True) for g in self.all(): @@ -373,7 +374,7 @@ class DeckManager: self.save(g) 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 self.save(grp) diff --git a/rslib/backend.proto b/rslib/backend.proto index 3eed0553b..421c6a21b 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -219,6 +219,7 @@ service CardRenderingService { service DeckConfigService { rpc AddOrUpdateDeckConfigLegacy(AddOrUpdateDeckConfigLegacyIn) returns (DeckConfigId); + rpc GetDeckConfig(DeckConfigId) returns (DeckConfig); rpc AllDeckConfigLegacy(Empty) returns (Json); rpc GetDeckConfigLegacy(DeckConfigId) returns (Json); rpc NewDeckConfigLegacy(Empty) returns (Json); @@ -294,71 +295,76 @@ service CardsService { // These should be moved to a separate file in the future /////////////////////////////////////////////////////////// -message DeckConfigInner { - enum NewCardOrder { - NEW_CARD_ORDER_DUE = 0; - NEW_CARD_ORDER_RANDOM = 1; - } - enum ReviewCardOrder { - REVIEW_CARD_ORDER_SHUFFLED_BY_DAY = 0; - REVIEW_CARD_ORDER_SHUFFLED = 1; - REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 2; - REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 3; - } - enum ReviewMix { - REVIEW_MIX_MIX_WITH_REVIEWS = 0; - REVIEW_MIX_AFTER_REVIEWS = 1; - REVIEW_MIX_BEFORE_REVIEWS = 2; - } - enum LeechAction { - LEECH_ACTION_SUSPEND = 0; - LEECH_ACTION_TAG_ONLY = 1; +message DeckConfig { + message Config { + enum NewCardOrder { + NEW_CARD_ORDER_DUE = 0; + NEW_CARD_ORDER_RANDOM = 1; + } + enum ReviewCardOrder { + REVIEW_CARD_ORDER_SHUFFLED_BY_DAY = 0; + REVIEW_CARD_ORDER_SHUFFLED = 1; + REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 2; + REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 3; + } + enum ReviewMix { + REVIEW_MIX_MIX_WITH_REVIEWS = 0; + REVIEW_MIX_AFTER_REVIEWS = 1; + REVIEW_MIX_BEFORE_REVIEWS = 2; + } + enum LeechAction { + LEECH_ACTION_SUSPEND = 0; + LEECH_ACTION_TAG_ONLY = 1; + } + + repeated float learn_steps = 1; + repeated float relearn_steps = 2; + + reserved 3 to 8; + + uint32 new_per_day = 9; + uint32 reviews_per_day = 10; + uint32 new_per_day_minimum = 29; + + float initial_ease = 11; + float easy_multiplier = 12; + float hard_multiplier = 13; + float lapse_multiplier = 14; + float interval_multiplier = 15; + + uint32 maximum_review_interval = 16; + uint32 minimum_lapse_interval = 17; + + uint32 graduating_interval_good = 18; + uint32 graduating_interval_easy = 19; + + NewCardOrder new_card_order = 20; + ReviewCardOrder review_order = 32; + + ReviewMix new_mix = 30; + ReviewMix interday_learning_mix = 31; + + LeechAction leech_action = 21; + uint32 leech_threshold = 22; + + bool disable_autoplay = 23; + uint32 cap_answer_time_to_secs = 24; + uint32 visible_timer_secs = 25; + bool skip_question_when_replaying_answer = 26; + + bool bury_new = 27; + bool bury_reviews = 28; + + bytes other = 255; } - repeated float learn_steps = 1; - repeated float relearn_steps = 2; - - reserved 3 to 8; - - uint32 new_per_day = 9; - uint32 reviews_per_day = 10; - uint32 new_per_day_minimum = 29; - - float initial_ease = 11; - float easy_multiplier = 12; - float hard_multiplier = 13; - float lapse_multiplier = 14; - float interval_multiplier = 15; - - uint32 maximum_review_interval = 16; - uint32 minimum_lapse_interval = 17; - - uint32 graduating_interval_good = 18; - uint32 graduating_interval_easy = 19; - - NewCardOrder new_card_order = 20; - ReviewCardOrder review_order = 32; - - ReviewMix new_mix = 30; - ReviewMix interday_learning_mix = 31; - - LeechAction leech_action = 21; - uint32 leech_threshold = 22; - - bool disable_autoplay = 23; - uint32 cap_answer_time_to_secs = 24; - uint32 visible_timer_secs = 25; - bool skip_question_when_replaying_answer = 26; - - bool bury_new = 27; - bool bury_reviews = 28; - - bytes other = 255; + int64 id = 1; + string name = 2; + int64 mtime_secs = 3; + int32 usn = 4; + Config config = 5; } -// Containers for passing around database objects -/////////////////////////////////////////////////////////// - message Deck { message Common { bool study_collapsed = 1; @@ -504,6 +510,9 @@ message Notetype { repeated Template templates = 9; } +// Database objects +/////////////////////////////////////////////////////////// + message Note { int64 id = 1; string guid = 2; diff --git a/rslib/src/backend/deckconfig.rs b/rslib/src/backend/deckconfig.rs index 4737959e8..76bbd9133 100644 --- a/rslib/src/backend/deckconfig.rs +++ b/rslib/src/backend/deckconfig.rs @@ -38,6 +38,10 @@ impl DeckConfigService for Backend { .map(Into::into) } + fn get_deck_config(&self, input: pb::DeckConfigId) -> Result { + self.with_col(|col| Ok(col.get_deck_config(input.into(), true)?.unwrap().into())) + } + fn get_deck_config_legacy(&self, input: pb::DeckConfigId) -> Result { self.with_col(|col| { let conf = col.get_deck_config(input.into(), true)?.unwrap(); @@ -58,3 +62,15 @@ impl DeckConfigService for Backend { .map(Into::into) } } + +impl From 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), + } + } +} diff --git a/rslib/src/deckconf/mod.rs b/rslib/src/deckconf/mod.rs index 41f13af0b..d5c593b9a 100644 --- a/rslib/src/deckconf/mod.rs +++ b/rslib/src/deckconf/mod.rs @@ -11,8 +11,8 @@ use crate::{ }; pub use crate::backend_proto::{ - deck_config_inner::{LeechAction, NewCardOrder, ReviewCardOrder, ReviewMix}, - DeckConfigInner, + deck_config::config::{LeechAction, NewCardOrder, ReviewCardOrder, ReviewMix}, + deck_config::Config as DeckConfigInner, }; pub use schema11::{DeckConfSchema11, NewCardOrderSchema11}; /// Old deck config and cards table store 250% as 2500. diff --git a/rslib/src/deckconf/schema11.rs b/rslib/src/deckconf/schema11.rs index 34693626d..e7edd25e5 100644 --- a/rslib/src/deckconf/schema11.rs +++ b/rslib/src/deckconf/schema11.rs @@ -2,8 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use super::{DeckConf, DeckConfId, INITIAL_EASE_FACTOR_THOUSANDS}; -use crate::backend_proto::deck_config_inner::NewCardOrder; -use crate::backend_proto::DeckConfigInner; +use super::{DeckConfigInner, NewCardOrder}; use crate::{serde::default_on_invalid, timestamp::TimestampSecs, types::Usn}; use serde_aux::field_attributes::deserialize_number_from_string; use serde_derive::{Deserialize, Serialize};