mirror of
https://github.com/ankitects/anki.git
synced 2025-12-20 10:22:57 -05:00
preserve mtime/usn when syncing deck config, and add snake_case names
This commit is contained in:
parent
d5f6d8b476
commit
333d0735ff
13 changed files with 90 additions and 48 deletions
|
|
@ -51,7 +51,7 @@ message BackendInput {
|
|||
Card update_card = 39;
|
||||
Card add_card = 40;
|
||||
int64 get_deck_config = 41;
|
||||
string add_or_update_deck_config = 42;
|
||||
AddOrUpdateDeckConfigIn add_or_update_deck_config = 42;
|
||||
Empty all_deck_config = 43;
|
||||
Empty new_deck_config = 44;
|
||||
int64 remove_deck_config = 45;
|
||||
|
|
@ -414,3 +414,8 @@ message Card {
|
|||
message CloseCollectionIn {
|
||||
bool downgrade_to_schema11 = 1;
|
||||
}
|
||||
|
||||
message AddOrUpdateDeckConfigIn {
|
||||
string config = 1;
|
||||
bool preserve_usn_and_mtime = 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class DeckManager:
|
|||
if g:
|
||||
# deck conf?
|
||||
if "maxTaken" in g:
|
||||
self.updateConf(g)
|
||||
self.update_config(g)
|
||||
return
|
||||
else:
|
||||
g["mod"] = intTime()
|
||||
|
|
@ -318,7 +318,7 @@ class DeckManager:
|
|||
# Deck configurations
|
||||
#############################################################
|
||||
|
||||
def allConf(self) -> List:
|
||||
def all_config(self) -> List:
|
||||
"A list of all deck config."
|
||||
return list(self.col.backend.all_deck_config())
|
||||
|
||||
|
|
@ -327,32 +327,38 @@ class DeckManager:
|
|||
assert deck
|
||||
if "conf" in deck:
|
||||
dcid = int(deck["conf"]) # may be a string
|
||||
conf = self.getConf(dcid)
|
||||
conf = self.get_config(dcid)
|
||||
conf["dyn"] = False
|
||||
return conf
|
||||
# dynamic decks have embedded conf
|
||||
return deck
|
||||
|
||||
def getConf(self, confId: int) -> Any:
|
||||
def get_config(self, conf_id: int) -> Any:
|
||||
if self._dconf_cache is not None:
|
||||
return self._dconf_cache.get(confId)
|
||||
return self.col.backend.get_deck_config(confId)
|
||||
return self._dconf_cache.get(conf_id)
|
||||
return self.col.backend.get_deck_config(conf_id)
|
||||
|
||||
def updateConf(self, g: Dict[str, Any]) -> None:
|
||||
self.col.backend.add_or_update_deck_config(g)
|
||||
def update_config(self, conf: Dict[str, Any], preserve_usn=False) -> None:
|
||||
self.col.backend.add_or_update_deck_config(conf, preserve_usn)
|
||||
|
||||
def confId(self, name: str, cloneFrom: Optional[Dict[str, Any]] = None) -> int:
|
||||
"Create a new configuration and return id."
|
||||
if cloneFrom is not None:
|
||||
conf = copy.deepcopy(cloneFrom)
|
||||
def add_config(
|
||||
self, name: str, clone_from: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
if clone_from is not None:
|
||||
conf = copy.deepcopy(clone_from)
|
||||
conf["id"] = 0
|
||||
else:
|
||||
conf = self.col.backend.new_deck_config()
|
||||
conf["name"] = name
|
||||
self.updateConf(conf)
|
||||
return conf["id"]
|
||||
self.update_config(conf)
|
||||
return conf
|
||||
|
||||
def remConf(self, id) -> None:
|
||||
def add_config_returning_id(
|
||||
self, name: str, clone_from: Optional[Dict[str, Any]] = None
|
||||
) -> int:
|
||||
return self.add_config(name, clone_from)["id"]
|
||||
|
||||
def remove_config(self, id) -> None:
|
||||
"Remove a configuration and update all decks using it."
|
||||
self.col.modSchema(check=True)
|
||||
for g in self.all():
|
||||
|
|
@ -380,14 +386,21 @@ class DeckManager:
|
|||
new = self.col.backend.new_deck_config()
|
||||
new["id"] = conf["id"]
|
||||
new["name"] = conf["name"]
|
||||
self.updateConf(new)
|
||||
self.update_config(new)
|
||||
# if it was previously randomized, re-sort
|
||||
if not oldOrder:
|
||||
self.col.sched.resortConf(new)
|
||||
|
||||
# legacy
|
||||
allConf = all_config
|
||||
getConf = get_config
|
||||
updateConf = update_config
|
||||
remConf = remove_config
|
||||
confId = add_config_returning_id
|
||||
|
||||
# temporary caching - don't use this as it will be removed
|
||||
def _enable_dconf_cache(self):
|
||||
self._dconf_cache = {c["id"]: c for c in self.allConf()}
|
||||
self._dconf_cache = {c["id"]: c for c in self.all_config()}
|
||||
|
||||
def _disable_dconf_cache(self):
|
||||
self._dconf_cache = None
|
||||
|
|
@ -613,8 +626,10 @@ class DeckManager:
|
|||
def beforeUpload(self) -> None:
|
||||
for d in self.all():
|
||||
d["usn"] = 0
|
||||
for c in self.allConf():
|
||||
for c in self.all_config():
|
||||
if c["usn"] != 0:
|
||||
c["usn"] = 0
|
||||
self.update_config(c, preserve_usn=True)
|
||||
self.save()
|
||||
|
||||
# Dynamic decks
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ class AnkiExporter(Exporter):
|
|||
# copy used deck confs
|
||||
for dc in self.src.decks.allConf():
|
||||
if dc["id"] in dconfs:
|
||||
self.dst.decks.updateConf(dc)
|
||||
self.dst.decks.update_config(dc)
|
||||
# find used media
|
||||
media = {}
|
||||
self.mediaDir = self.src.media.dir()
|
||||
|
|
|
|||
|
|
@ -277,9 +277,9 @@ class Anki2Importer(Importer):
|
|||
newid = self.dst.decks.id(name)
|
||||
# pull conf over
|
||||
if "conf" in g and g["conf"] != 1:
|
||||
conf = self.src.decks.getConf(g["conf"])
|
||||
conf = self.src.decks.get_config(g["conf"])
|
||||
self.dst.decks.save(conf)
|
||||
self.dst.decks.updateConf(conf)
|
||||
self.dst.decks.update_config(conf)
|
||||
g2 = self.dst.decks.get(newid)
|
||||
g2["conf"] = g["conf"]
|
||||
self.dst.decks.save(g2)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
# pylint: skip-file
|
||||
|
||||
"""
|
||||
Python bindings for Anki's Rust libraries.
|
||||
|
||||
Please do not access methods on the backend directly - they may be changed
|
||||
or removed at any time. Instead, please use the methods on the collection
|
||||
instead. Eg, don't use col.backend.all_deck_config(), instead use
|
||||
col.decks.all_config()
|
||||
"""
|
||||
|
||||
import enum
|
||||
import json
|
||||
import os
|
||||
|
|
@ -502,10 +511,14 @@ class RustBackend:
|
|||
jstr = self._run_command(pb.BackendInput(get_deck_config=dcid)).get_deck_config
|
||||
return json.loads(jstr)
|
||||
|
||||
def add_or_update_deck_config(self, conf: Dict[str, Any]) -> None:
|
||||
def add_or_update_deck_config(self, conf: Dict[str, Any], preserve_usn) -> None:
|
||||
conf_json = json.dumps(conf)
|
||||
id = self._run_command(
|
||||
pb.BackendInput(add_or_update_deck_config=conf_json)
|
||||
pb.BackendInput(
|
||||
add_or_update_deck_config=pb.AddOrUpdateDeckConfigIn(
|
||||
config=conf_json, preserve_usn_and_mtime=preserve_usn
|
||||
)
|
||||
)
|
||||
).add_or_update_deck_config
|
||||
conf["id"] = id
|
||||
|
||||
|
|
|
|||
|
|
@ -374,9 +374,10 @@ from notes where %s"""
|
|||
decks = [g for g in self.col.decks.all() if g["usn"] == -1]
|
||||
for g in decks:
|
||||
g["usn"] = self.maxUsn
|
||||
dconf = [g for g in self.col.decks.allConf() if g["usn"] == -1]
|
||||
dconf = [g for g in self.col.decks.all_config() if g["usn"] == -1]
|
||||
for g in dconf:
|
||||
g["usn"] = self.maxUsn
|
||||
self.col.decks.update_config(g, preserve_usn=True)
|
||||
self.col.decks.save()
|
||||
return [decks, dconf]
|
||||
|
||||
|
|
@ -392,12 +393,12 @@ from notes where %s"""
|
|||
self.col.decks.update(r)
|
||||
for r in rchg[1]:
|
||||
try:
|
||||
l = self.col.decks.getConf(r["id"])
|
||||
l = self.col.decks.get_config(r["id"])
|
||||
except KeyError:
|
||||
l = None
|
||||
# if missing locally or server is newer, update
|
||||
if not l or r["mod"] > l["mod"]:
|
||||
self.col.decks.updateConf(r)
|
||||
self.col.decks.update_config(r)
|
||||
|
||||
# Tags
|
||||
##########################################################################
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ def test_export_anki():
|
|||
# create a new deck with its own conf to test conf copying
|
||||
did = deck.decks.id("test")
|
||||
dobj = deck.decks.get(did)
|
||||
confId = deck.decks.confId("newconf")
|
||||
conf = deck.decks.getConf(confId)
|
||||
confId = deck.decks.add_config_returning_id("newconf")
|
||||
conf = deck.decks.get_config(confId)
|
||||
conf["new"]["perDay"] = 5
|
||||
deck.decks.save(conf)
|
||||
deck.decks.setConf(dobj, confId)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ def test_newLimits():
|
|||
f.model()["did"] = g2
|
||||
d.addNote(f)
|
||||
# give the child deck a different configuration
|
||||
c2 = d.decks.confId("new conf")
|
||||
c2 = d.decks.add_config_returning_id("new conf")
|
||||
d.decks.setConf(d.decks.get(g2), c2)
|
||||
d.reset()
|
||||
# both confs have defaulted to a limit of 20
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ def test_newLimits():
|
|||
f.model()["did"] = g2
|
||||
d.addNote(f)
|
||||
# give the child deck a different configuration
|
||||
c2 = d.decks.confId("new conf")
|
||||
c2 = d.decks.add_config_returning_id("new conf")
|
||||
d.decks.setConf(d.decks.get(g2), c2)
|
||||
d.reset()
|
||||
# both confs have defaulted to a limit of 20
|
||||
|
|
@ -412,14 +412,14 @@ def test_review_limits():
|
|||
parent = d.decks.get(d.decks.id("parent"))
|
||||
child = d.decks.get(d.decks.id("parent::child"))
|
||||
|
||||
pconf = d.decks.getConf(d.decks.confId("parentConf"))
|
||||
cconf = d.decks.getConf(d.decks.confId("childConf"))
|
||||
pconf = d.decks.get_config(d.decks.add_config_returning_id("parentConf"))
|
||||
cconf = d.decks.get_config(d.decks.add_config_returning_id("childConf"))
|
||||
|
||||
pconf["rev"]["perDay"] = 5
|
||||
d.decks.updateConf(pconf)
|
||||
d.decks.update_config(pconf)
|
||||
d.decks.setConf(parent, pconf["id"])
|
||||
cconf["rev"]["perDay"] = 10
|
||||
d.decks.updateConf(cconf)
|
||||
d.decks.update_config(cconf)
|
||||
d.decks.setConf(child, cconf["id"])
|
||||
|
||||
m = d.models.current()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CustomStudy(QDialog):
|
|||
QDialog.__init__(self, mw)
|
||||
self.mw = mw
|
||||
self.deck = self.mw.col.decks.current()
|
||||
self.conf = self.mw.col.decks.getConf(self.deck["conf"])
|
||||
self.conf = self.mw.col.decks.get_config(self.deck["conf"])
|
||||
self.form = f = aqt.forms.customstudy.Ui_Dialog()
|
||||
self.created_custom_study = False
|
||||
f.setupUi(self)
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class DeckConf(QDialog):
|
|||
# first, save currently entered data to current conf
|
||||
self.saveConf()
|
||||
# then clone the conf
|
||||
id = self.mw.col.decks.confId(name, cloneFrom=self.conf)
|
||||
id = self.mw.col.decks.add_config_returning_id(name, cloneFrom=self.conf)
|
||||
# set the deck to the new conf
|
||||
self.deck["conf"] = id
|
||||
# then reload the conf list
|
||||
|
|
@ -133,7 +133,7 @@ class DeckConf(QDialog):
|
|||
if int(self.conf["id"]) == 1:
|
||||
showInfo(_("The default configuration can't be removed."), self)
|
||||
else:
|
||||
self.mw.col.decks.remConf(self.conf["id"])
|
||||
self.mw.col.decks.remove_config(self.conf["id"])
|
||||
self.deck["conf"] = 1
|
||||
self.loadConfs()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
use crate::backend::dbproxy::db_command_bytes;
|
||||
use crate::backend_proto::backend_input::Value;
|
||||
use crate::backend_proto::{BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn};
|
||||
use crate::backend_proto::{
|
||||
AddOrUpdateDeckConfigIn, BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn,
|
||||
};
|
||||
use crate::card::{Card, CardID};
|
||||
use crate::card::{CardQueue, CardType};
|
||||
use crate::collection::{open_collection, Collection};
|
||||
|
|
@ -268,8 +270,8 @@ impl Backend {
|
|||
}
|
||||
Value::AddCard(card) => OValue::AddCard(self.add_card(card)?),
|
||||
Value::GetDeckConfig(dcid) => OValue::GetDeckConfig(self.get_deck_config(dcid)?),
|
||||
Value::AddOrUpdateDeckConfig(conf_json) => {
|
||||
OValue::AddOrUpdateDeckConfig(self.add_or_update_deck_config(conf_json)?)
|
||||
Value::AddOrUpdateDeckConfig(input) => {
|
||||
OValue::AddOrUpdateDeckConfig(self.add_or_update_deck_config(input)?)
|
||||
}
|
||||
Value::AllDeckConfig(_) => OValue::AllDeckConfig(self.all_deck_config()?),
|
||||
Value::NewDeckConfig(_) => OValue::NewDeckConfig(self.new_deck_config()?),
|
||||
|
|
@ -702,11 +704,11 @@ impl Backend {
|
|||
})
|
||||
}
|
||||
|
||||
fn add_or_update_deck_config(&self, conf_json: String) -> Result<i64> {
|
||||
let mut conf: DeckConf = serde_json::from_str(&conf_json)?;
|
||||
fn add_or_update_deck_config(&self, input: AddOrUpdateDeckConfigIn) -> Result<i64> {
|
||||
let mut conf: DeckConf = serde_json::from_str(&input.config)?;
|
||||
self.with_col(|col| {
|
||||
col.transact(None, |col| {
|
||||
col.add_or_update_deck_config(&mut conf)?;
|
||||
col.add_or_update_deck_config(&mut conf, input.preserve_usn_and_mtime)?;
|
||||
Ok(conf.id.0)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -219,9 +219,15 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_or_update_deck_config(&self, conf: &mut DeckConf) -> Result<()> {
|
||||
pub(crate) fn add_or_update_deck_config(
|
||||
&self,
|
||||
conf: &mut DeckConf,
|
||||
preserve_usn_and_mtime: bool,
|
||||
) -> Result<()> {
|
||||
if !preserve_usn_and_mtime {
|
||||
conf.mtime = TimestampSecs::now();
|
||||
conf.usn = self.usn()?;
|
||||
}
|
||||
let orig = self.storage.get_deck_config(conf.id)?;
|
||||
if let Some(_orig) = orig {
|
||||
self.storage.update_deck_conf(&conf)
|
||||
|
|
|
|||
Loading…
Reference in a new issue