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 update_card = 39;
|
||||||
Card add_card = 40;
|
Card add_card = 40;
|
||||||
int64 get_deck_config = 41;
|
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 all_deck_config = 43;
|
||||||
Empty new_deck_config = 44;
|
Empty new_deck_config = 44;
|
||||||
int64 remove_deck_config = 45;
|
int64 remove_deck_config = 45;
|
||||||
|
|
@ -414,3 +414,8 @@ message Card {
|
||||||
message CloseCollectionIn {
|
message CloseCollectionIn {
|
||||||
bool downgrade_to_schema11 = 1;
|
bool downgrade_to_schema11 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AddOrUpdateDeckConfigIn {
|
||||||
|
string config = 1;
|
||||||
|
bool preserve_usn_and_mtime = 2;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class DeckManager:
|
||||||
if g:
|
if g:
|
||||||
# deck conf?
|
# deck conf?
|
||||||
if "maxTaken" in g:
|
if "maxTaken" in g:
|
||||||
self.updateConf(g)
|
self.update_config(g)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
g["mod"] = intTime()
|
g["mod"] = intTime()
|
||||||
|
|
@ -318,7 +318,7 @@ class DeckManager:
|
||||||
# Deck configurations
|
# Deck configurations
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def allConf(self) -> List:
|
def all_config(self) -> List:
|
||||||
"A list of all deck config."
|
"A list of all deck config."
|
||||||
return list(self.col.backend.all_deck_config())
|
return list(self.col.backend.all_deck_config())
|
||||||
|
|
||||||
|
|
@ -327,32 +327,38 @@ class DeckManager:
|
||||||
assert deck
|
assert deck
|
||||||
if "conf" in deck:
|
if "conf" in deck:
|
||||||
dcid = int(deck["conf"]) # may be a string
|
dcid = int(deck["conf"]) # may be a string
|
||||||
conf = self.getConf(dcid)
|
conf = self.get_config(dcid)
|
||||||
conf["dyn"] = False
|
conf["dyn"] = False
|
||||||
return conf
|
return conf
|
||||||
# dynamic decks have embedded conf
|
# dynamic decks have embedded conf
|
||||||
return deck
|
return deck
|
||||||
|
|
||||||
def getConf(self, confId: int) -> Any:
|
def get_config(self, conf_id: int) -> Any:
|
||||||
if self._dconf_cache is not None:
|
if self._dconf_cache is not None:
|
||||||
return self._dconf_cache.get(confId)
|
return self._dconf_cache.get(conf_id)
|
||||||
return self.col.backend.get_deck_config(confId)
|
return self.col.backend.get_deck_config(conf_id)
|
||||||
|
|
||||||
def updateConf(self, g: Dict[str, Any]) -> None:
|
def update_config(self, conf: Dict[str, Any], preserve_usn=False) -> None:
|
||||||
self.col.backend.add_or_update_deck_config(g)
|
self.col.backend.add_or_update_deck_config(conf, preserve_usn)
|
||||||
|
|
||||||
def confId(self, name: str, cloneFrom: Optional[Dict[str, Any]] = None) -> int:
|
def add_config(
|
||||||
"Create a new configuration and return id."
|
self, name: str, clone_from: Optional[Dict[str, Any]] = None
|
||||||
if cloneFrom is not None:
|
) -> Dict[str, Any]:
|
||||||
conf = copy.deepcopy(cloneFrom)
|
if clone_from is not None:
|
||||||
|
conf = copy.deepcopy(clone_from)
|
||||||
conf["id"] = 0
|
conf["id"] = 0
|
||||||
else:
|
else:
|
||||||
conf = self.col.backend.new_deck_config()
|
conf = self.col.backend.new_deck_config()
|
||||||
conf["name"] = name
|
conf["name"] = name
|
||||||
self.updateConf(conf)
|
self.update_config(conf)
|
||||||
return conf["id"]
|
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."
|
"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():
|
||||||
|
|
@ -380,14 +386,21 @@ class DeckManager:
|
||||||
new = self.col.backend.new_deck_config()
|
new = self.col.backend.new_deck_config()
|
||||||
new["id"] = conf["id"]
|
new["id"] = conf["id"]
|
||||||
new["name"] = conf["name"]
|
new["name"] = conf["name"]
|
||||||
self.updateConf(new)
|
self.update_config(new)
|
||||||
# if it was previously randomized, re-sort
|
# if it was previously randomized, re-sort
|
||||||
if not oldOrder:
|
if not oldOrder:
|
||||||
self.col.sched.resortConf(new)
|
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
|
# temporary caching - don't use this as it will be removed
|
||||||
def _enable_dconf_cache(self):
|
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):
|
def _disable_dconf_cache(self):
|
||||||
self._dconf_cache = None
|
self._dconf_cache = None
|
||||||
|
|
@ -613,8 +626,10 @@ class DeckManager:
|
||||||
def beforeUpload(self) -> None:
|
def beforeUpload(self) -> None:
|
||||||
for d in self.all():
|
for d in self.all():
|
||||||
d["usn"] = 0
|
d["usn"] = 0
|
||||||
for c in self.allConf():
|
for c in self.all_config():
|
||||||
c["usn"] = 0
|
if c["usn"] != 0:
|
||||||
|
c["usn"] = 0
|
||||||
|
self.update_config(c, preserve_usn=True)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
# Dynamic decks
|
# Dynamic decks
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ class AnkiExporter(Exporter):
|
||||||
# copy used deck confs
|
# copy used deck confs
|
||||||
for dc in self.src.decks.allConf():
|
for dc in self.src.decks.allConf():
|
||||||
if dc["id"] in dconfs:
|
if dc["id"] in dconfs:
|
||||||
self.dst.decks.updateConf(dc)
|
self.dst.decks.update_config(dc)
|
||||||
# find used media
|
# find used media
|
||||||
media = {}
|
media = {}
|
||||||
self.mediaDir = self.src.media.dir()
|
self.mediaDir = self.src.media.dir()
|
||||||
|
|
|
||||||
|
|
@ -277,9 +277,9 @@ class Anki2Importer(Importer):
|
||||||
newid = self.dst.decks.id(name)
|
newid = self.dst.decks.id(name)
|
||||||
# pull conf over
|
# pull conf over
|
||||||
if "conf" in g and g["conf"] != 1:
|
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.save(conf)
|
||||||
self.dst.decks.updateConf(conf)
|
self.dst.decks.update_config(conf)
|
||||||
g2 = self.dst.decks.get(newid)
|
g2 = self.dst.decks.get(newid)
|
||||||
g2["conf"] = g["conf"]
|
g2["conf"] = g["conf"]
|
||||||
self.dst.decks.save(g2)
|
self.dst.decks.save(g2)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,15 @@
|
||||||
# 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
|
||||||
# pylint: skip-file
|
# 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 enum
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -502,10 +511,14 @@ class RustBackend:
|
||||||
jstr = self._run_command(pb.BackendInput(get_deck_config=dcid)).get_deck_config
|
jstr = self._run_command(pb.BackendInput(get_deck_config=dcid)).get_deck_config
|
||||||
return json.loads(jstr)
|
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)
|
conf_json = json.dumps(conf)
|
||||||
id = self._run_command(
|
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
|
).add_or_update_deck_config
|
||||||
conf["id"] = id
|
conf["id"] = id
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -374,9 +374,10 @@ from notes where %s"""
|
||||||
decks = [g for g in self.col.decks.all() if g["usn"] == -1]
|
decks = [g for g in self.col.decks.all() if g["usn"] == -1]
|
||||||
for g in decks:
|
for g in decks:
|
||||||
g["usn"] = self.maxUsn
|
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:
|
for g in dconf:
|
||||||
g["usn"] = self.maxUsn
|
g["usn"] = self.maxUsn
|
||||||
|
self.col.decks.update_config(g, preserve_usn=True)
|
||||||
self.col.decks.save()
|
self.col.decks.save()
|
||||||
return [decks, dconf]
|
return [decks, dconf]
|
||||||
|
|
||||||
|
|
@ -392,12 +393,12 @@ from notes where %s"""
|
||||||
self.col.decks.update(r)
|
self.col.decks.update(r)
|
||||||
for r in rchg[1]:
|
for r in rchg[1]:
|
||||||
try:
|
try:
|
||||||
l = self.col.decks.getConf(r["id"])
|
l = self.col.decks.get_config(r["id"])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
l = None
|
l = None
|
||||||
# if missing locally or server is newer, update
|
# if missing locally or server is newer, update
|
||||||
if not l or r["mod"] > l["mod"]:
|
if not l or r["mod"] > l["mod"]:
|
||||||
self.col.decks.updateConf(r)
|
self.col.decks.update_config(r)
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ def test_export_anki():
|
||||||
# create a new deck with its own conf to test conf copying
|
# create a new deck with its own conf to test conf copying
|
||||||
did = deck.decks.id("test")
|
did = deck.decks.id("test")
|
||||||
dobj = deck.decks.get(did)
|
dobj = deck.decks.get(did)
|
||||||
confId = deck.decks.confId("newconf")
|
confId = deck.decks.add_config_returning_id("newconf")
|
||||||
conf = deck.decks.getConf(confId)
|
conf = deck.decks.get_config(confId)
|
||||||
conf["new"]["perDay"] = 5
|
conf["new"]["perDay"] = 5
|
||||||
deck.decks.save(conf)
|
deck.decks.save(conf)
|
||||||
deck.decks.setConf(dobj, confId)
|
deck.decks.setConf(dobj, confId)
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ def test_newLimits():
|
||||||
f.model()["did"] = g2
|
f.model()["did"] = g2
|
||||||
d.addNote(f)
|
d.addNote(f)
|
||||||
# give the child deck a different configuration
|
# 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.decks.setConf(d.decks.get(g2), c2)
|
||||||
d.reset()
|
d.reset()
|
||||||
# both confs have defaulted to a limit of 20
|
# both confs have defaulted to a limit of 20
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ def test_newLimits():
|
||||||
f.model()["did"] = g2
|
f.model()["did"] = g2
|
||||||
d.addNote(f)
|
d.addNote(f)
|
||||||
# give the child deck a different configuration
|
# 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.decks.setConf(d.decks.get(g2), c2)
|
||||||
d.reset()
|
d.reset()
|
||||||
# both confs have defaulted to a limit of 20
|
# 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"))
|
parent = d.decks.get(d.decks.id("parent"))
|
||||||
child = d.decks.get(d.decks.id("parent::child"))
|
child = d.decks.get(d.decks.id("parent::child"))
|
||||||
|
|
||||||
pconf = d.decks.getConf(d.decks.confId("parentConf"))
|
pconf = d.decks.get_config(d.decks.add_config_returning_id("parentConf"))
|
||||||
cconf = d.decks.getConf(d.decks.confId("childConf"))
|
cconf = d.decks.get_config(d.decks.add_config_returning_id("childConf"))
|
||||||
|
|
||||||
pconf["rev"]["perDay"] = 5
|
pconf["rev"]["perDay"] = 5
|
||||||
d.decks.updateConf(pconf)
|
d.decks.update_config(pconf)
|
||||||
d.decks.setConf(parent, pconf["id"])
|
d.decks.setConf(parent, pconf["id"])
|
||||||
cconf["rev"]["perDay"] = 10
|
cconf["rev"]["perDay"] = 10
|
||||||
d.decks.updateConf(cconf)
|
d.decks.update_config(cconf)
|
||||||
d.decks.setConf(child, cconf["id"])
|
d.decks.setConf(child, cconf["id"])
|
||||||
|
|
||||||
m = d.models.current()
|
m = d.models.current()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class CustomStudy(QDialog):
|
||||||
QDialog.__init__(self, mw)
|
QDialog.__init__(self, mw)
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.deck = self.mw.col.decks.current()
|
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.form = f = aqt.forms.customstudy.Ui_Dialog()
|
||||||
self.created_custom_study = False
|
self.created_custom_study = False
|
||||||
f.setupUi(self)
|
f.setupUi(self)
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ class DeckConf(QDialog):
|
||||||
# first, save currently entered data to current conf
|
# first, save currently entered data to current conf
|
||||||
self.saveConf()
|
self.saveConf()
|
||||||
# then clone the conf
|
# 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
|
# set the deck to the new conf
|
||||||
self.deck["conf"] = id
|
self.deck["conf"] = id
|
||||||
# then reload the conf list
|
# then reload the conf list
|
||||||
|
|
@ -133,7 +133,7 @@ class DeckConf(QDialog):
|
||||||
if int(self.conf["id"]) == 1:
|
if int(self.conf["id"]) == 1:
|
||||||
showInfo(_("The default configuration can't be removed."), self)
|
showInfo(_("The default configuration can't be removed."), self)
|
||||||
else:
|
else:
|
||||||
self.mw.col.decks.remConf(self.conf["id"])
|
self.mw.col.decks.remove_config(self.conf["id"])
|
||||||
self.deck["conf"] = 1
|
self.deck["conf"] = 1
|
||||||
self.loadConfs()
|
self.loadConfs()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
use crate::backend::dbproxy::db_command_bytes;
|
use crate::backend::dbproxy::db_command_bytes;
|
||||||
use crate::backend_proto::backend_input::Value;
|
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::{Card, CardID};
|
||||||
use crate::card::{CardQueue, CardType};
|
use crate::card::{CardQueue, CardType};
|
||||||
use crate::collection::{open_collection, Collection};
|
use crate::collection::{open_collection, Collection};
|
||||||
|
|
@ -268,8 +270,8 @@ impl Backend {
|
||||||
}
|
}
|
||||||
Value::AddCard(card) => OValue::AddCard(self.add_card(card)?),
|
Value::AddCard(card) => OValue::AddCard(self.add_card(card)?),
|
||||||
Value::GetDeckConfig(dcid) => OValue::GetDeckConfig(self.get_deck_config(dcid)?),
|
Value::GetDeckConfig(dcid) => OValue::GetDeckConfig(self.get_deck_config(dcid)?),
|
||||||
Value::AddOrUpdateDeckConfig(conf_json) => {
|
Value::AddOrUpdateDeckConfig(input) => {
|
||||||
OValue::AddOrUpdateDeckConfig(self.add_or_update_deck_config(conf_json)?)
|
OValue::AddOrUpdateDeckConfig(self.add_or_update_deck_config(input)?)
|
||||||
}
|
}
|
||||||
Value::AllDeckConfig(_) => OValue::AllDeckConfig(self.all_deck_config()?),
|
Value::AllDeckConfig(_) => OValue::AllDeckConfig(self.all_deck_config()?),
|
||||||
Value::NewDeckConfig(_) => OValue::NewDeckConfig(self.new_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> {
|
fn add_or_update_deck_config(&self, input: AddOrUpdateDeckConfigIn) -> Result<i64> {
|
||||||
let mut conf: DeckConf = serde_json::from_str(&conf_json)?;
|
let mut conf: DeckConf = serde_json::from_str(&input.config)?;
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.transact(None, |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)
|
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(
|
||||||
conf.mtime = TimestampSecs::now();
|
&self,
|
||||||
conf.usn = self.usn()?;
|
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)?;
|
let orig = self.storage.get_deck_config(conf.id)?;
|
||||||
if let Some(_orig) = orig {
|
if let Some(_orig) = orig {
|
||||||
self.storage.update_deck_conf(&conf)
|
self.storage.update_deck_conf(&conf)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue