find & replace

This commit is contained in:
Damien Elmes 2011-04-14 03:10:50 +09:00
parent 98a63285e1
commit d51cd5a433
3 changed files with 64 additions and 38 deletions

View file

@ -15,7 +15,7 @@ from anki.errors import AnkiError
import anki.latex # sets up hook
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
# 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):
import anki.find
return anki.find.Finder(self).findCards(query)
def findReplace(self, *args, **kwargs):
import anki.find
return anki.find.findReplace(self, *args, **kwargs)
def findReplace(self, fids, src, dst, regex=None, field=None):
return anki.find.findReplace(self, fids, src, dst, regex, field)
def findDuplicates(self, fmids):
import anki.find
return anki.find.findDuplicates(self, fmids)
# Stats

View file

@ -3,7 +3,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import re
from anki.utils import ids2str, splitFields
from anki.utils import ids2str, splitFields, joinFields
SEARCH_TAG = 0
SEARCH_TYPE = 1
@ -344,41 +344,43 @@ where mid in %s and flds like ? escape '\\'""" % (
# 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
s = "select id, fid, value from fdata where fid in %s"
if isRe:
isRe = re.compile(src)
else:
s += " and value like :v"
mmap = {}
if field:
s += " and fmid = :fmid"
rows = deck.db.all(s % ids2str(fids),
v="%"+src.replace("%", "%%")+"%",
fmid=field)
modded = []
if isRe:
modded = [
{'id': id, 'fid': fid, 'val': re.sub(isRe, dst, val)}
for (id, fid, val) in rows
if isRe.search(val)]
else:
modded = [
{'id': id, 'fid': fid, 'val': val.replace(src, dst)}
for (id, fid, val) in rows
if val.find(src) != -1]
# update
if modded:
deck.db.executemany(
'update fdata set value = :val where id = :id', modded)
deck.updateCardQACacheFromIds([f['fid'] for f in modded],
type="facts")
for m in deck.models().values():
for f in m.fields:
if f['name'] == field:
mmap[m.id] = f['ord']
if not mmap:
return 0
# find and gather replacements
if not regex:
src = re.escape(src)
regex = re.compile("(?i)"+src)
def repl(str):
return re.sub(regex, dst, str)
d = []
for fid, mid, flds in deck.db.execute(
"select id, mid, flds from facts where id in "+ids2str(fids)):
origFlds = flds
# does it match?
sflds = splitFields(flds)
if field:
deck.updateFieldChecksums(field)
ord = mmap[mid]
sflds[ord] = repl(sflds[ord])
else:
deck.updateAllFieldChecksums()
return len(set([f['fid'] for f in modded]))
for c in range(len(sflds)):
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
##########################################################################

View file

@ -101,3 +101,30 @@ def test_findCards():
assert len(deck.findCards("group:default")) == 5
assert len(deck.findCards("-group:default")) == 0
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"