mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 15:32:23 -04:00
find & replace
This commit is contained in:
parent
98a63285e1
commit
d51cd5a433
3 changed files with 64 additions and 38 deletions
|
@ -15,7 +15,7 @@ from anki.errors import AnkiError
|
||||||
|
|
||||||
import anki.latex # sets up hook
|
import anki.latex # sets up hook
|
||||||
import anki.cards, anki.facts, anki.models, anki.template, anki.cram, \
|
import anki.cards, anki.facts, anki.models, anki.template, anki.cram, \
|
||||||
anki.groups
|
anki.groups, anki.find
|
||||||
|
|
||||||
# Settings related to queue building. These may be loaded without the rest of
|
# Settings related to queue building. These may be loaded without the rest of
|
||||||
# the config to check due counts faster on mobile clients.
|
# the config to check due counts faster on mobile clients.
|
||||||
|
@ -641,15 +641,12 @@ update facts set tags = :t, mod = :n where id = :id""", [fix(row) for row in res
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def findCards(self, query):
|
def findCards(self, query):
|
||||||
import anki.find
|
|
||||||
return anki.find.Finder(self).findCards(query)
|
return anki.find.Finder(self).findCards(query)
|
||||||
|
|
||||||
def findReplace(self, *args, **kwargs):
|
def findReplace(self, fids, src, dst, regex=None, field=None):
|
||||||
import anki.find
|
return anki.find.findReplace(self, fids, src, dst, regex, field)
|
||||||
return anki.find.findReplace(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def findDuplicates(self, fmids):
|
def findDuplicates(self, fmids):
|
||||||
import anki.find
|
|
||||||
return anki.find.findDuplicates(self, fmids)
|
return anki.find.findDuplicates(self, fmids)
|
||||||
|
|
||||||
# Stats
|
# Stats
|
||||||
|
|
66
anki/find.py
66
anki/find.py
|
@ -3,7 +3,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
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from anki.utils import ids2str, splitFields
|
from anki.utils import ids2str, splitFields, joinFields
|
||||||
|
|
||||||
SEARCH_TAG = 0
|
SEARCH_TAG = 0
|
||||||
SEARCH_TYPE = 1
|
SEARCH_TYPE = 1
|
||||||
|
@ -344,41 +344,43 @@ where mid in %s and flds like ? escape '\\'""" % (
|
||||||
# Find and replace
|
# Find and replace
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def findReplace(deck, fids, src, dst, isRe=False, field=None):
|
def findReplace(deck, fids, src, dst, regex=False, field=None):
|
||||||
"Find and replace fields in a fact."
|
"Find and replace fields in a fact."
|
||||||
# find
|
mmap = {}
|
||||||
s = "select id, fid, value from fdata where fid in %s"
|
|
||||||
if isRe:
|
|
||||||
isRe = re.compile(src)
|
|
||||||
else:
|
|
||||||
s += " and value like :v"
|
|
||||||
if field:
|
if field:
|
||||||
s += " and fmid = :fmid"
|
for m in deck.models().values():
|
||||||
rows = deck.db.all(s % ids2str(fids),
|
for f in m.fields:
|
||||||
v="%"+src.replace("%", "%%")+"%",
|
if f['name'] == field:
|
||||||
fmid=field)
|
mmap[m.id] = f['ord']
|
||||||
modded = []
|
if not mmap:
|
||||||
if isRe:
|
return 0
|
||||||
modded = [
|
# find and gather replacements
|
||||||
{'id': id, 'fid': fid, 'val': re.sub(isRe, dst, val)}
|
if not regex:
|
||||||
for (id, fid, val) in rows
|
src = re.escape(src)
|
||||||
if isRe.search(val)]
|
regex = re.compile("(?i)"+src)
|
||||||
else:
|
def repl(str):
|
||||||
modded = [
|
return re.sub(regex, dst, str)
|
||||||
{'id': id, 'fid': fid, 'val': val.replace(src, dst)}
|
d = []
|
||||||
for (id, fid, val) in rows
|
for fid, mid, flds in deck.db.execute(
|
||||||
if val.find(src) != -1]
|
"select id, mid, flds from facts where id in "+ids2str(fids)):
|
||||||
# update
|
origFlds = flds
|
||||||
if modded:
|
# does it match?
|
||||||
deck.db.executemany(
|
sflds = splitFields(flds)
|
||||||
'update fdata set value = :val where id = :id', modded)
|
|
||||||
deck.updateCardQACacheFromIds([f['fid'] for f in modded],
|
|
||||||
type="facts")
|
|
||||||
if field:
|
if field:
|
||||||
deck.updateFieldChecksums(field)
|
ord = mmap[mid]
|
||||||
|
sflds[ord] = repl(sflds[ord])
|
||||||
else:
|
else:
|
||||||
deck.updateAllFieldChecksums()
|
for c in range(len(sflds)):
|
||||||
return len(set([f['fid'] for f in modded]))
|
sflds[c] = repl(sflds[c])
|
||||||
|
flds = joinFields(sflds)
|
||||||
|
if flds != origFlds:
|
||||||
|
d.append(dict(fid=fid, flds=flds))
|
||||||
|
if not d:
|
||||||
|
return 0
|
||||||
|
# replace
|
||||||
|
deck.db.executemany("update facts set flds = :flds where id=:fid", d)
|
||||||
|
deck.updateFieldCache(fids)
|
||||||
|
return len(d)
|
||||||
|
|
||||||
# Find duplicates
|
# Find duplicates
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -101,3 +101,30 @@ def test_findCards():
|
||||||
assert len(deck.findCards("group:default")) == 5
|
assert len(deck.findCards("group:default")) == 5
|
||||||
assert len(deck.findCards("-group:default")) == 0
|
assert len(deck.findCards("-group:default")) == 0
|
||||||
assert len(deck.findCards("-group:foo")) == 5
|
assert len(deck.findCards("-group:foo")) == 5
|
||||||
|
|
||||||
|
def test_findReplace():
|
||||||
|
deck = getEmptyDeck()
|
||||||
|
f = deck.newFact()
|
||||||
|
f['Front'] = u'foo'
|
||||||
|
f['Back'] = u'bar'
|
||||||
|
deck.addFact(f)
|
||||||
|
f2 = deck.newFact()
|
||||||
|
f2['Front'] = u'baz'
|
||||||
|
f2['Back'] = u'foo'
|
||||||
|
deck.addFact(f2)
|
||||||
|
fids = [f.id, f2.id]
|
||||||
|
# should do nothing
|
||||||
|
assert deck.findReplace(fids, "abc", "123") == 0
|
||||||
|
# global replace
|
||||||
|
assert deck.findReplace(fids, "foo", "qux") == 2
|
||||||
|
f.load(); assert f['Front'] == "qux"
|
||||||
|
f2.load(); assert f2['Back'] == "qux"
|
||||||
|
# single field replace
|
||||||
|
assert deck.findReplace(fids, "qux", "foo", field="Front") == 1
|
||||||
|
f.load(); assert f['Front'] == "foo"
|
||||||
|
f2.load(); assert f2['Back'] == "qux"
|
||||||
|
# regex replace
|
||||||
|
assert deck.findReplace(fids, "B.r", "reg") == 0
|
||||||
|
f.load(); assert f['Back'] != "reg"
|
||||||
|
assert deck.findReplace(fids, "B.r", "reg", regex=True) == 1
|
||||||
|
f.load(); assert f['Back'] == "reg"
|
||||||
|
|
Loading…
Reference in a new issue