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.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

View file

@ -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
########################################################################## ##########################################################################

View file

@ -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"