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.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
|
||||
|
|
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
|
||||
|
||||
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
|
||||
##########################################################################
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue