Merge pull request #352 from Arthur-Milchior/correctCaseInRename

Ensure that even childless deck's name is considered independtly of the case
This commit is contained in:
Damien Elmes 2019-11-14 09:25:34 +10:00 committed by GitHub
commit ea9e564b66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 10 deletions

View File

@ -135,9 +135,9 @@ class DeckManager:
type = defaultDeck type = defaultDeck
name = name.replace('"', '') name = name.replace('"', '')
name = unicodedata.normalize("NFC", name) name = unicodedata.normalize("NFC", name)
for id, g in list(self.decks.items()): deck = self.byName(name)
if unicodedata.normalize("NFC", g['name'].lower()) == name.lower(): if deck:
return int(id) return int(deck["id"])
if not create: if not create:
return None return None
g = copy.deepcopy(type) g = copy.deepcopy(type)
@ -242,9 +242,9 @@ class DeckManager:
return self.decks['1'] return self.decks['1']
def byName(self, name): def byName(self, name):
"Get deck with NAME." """Get deck with NAME, ignoring cases."""
for m in list(self.decks.values()): for m in list(self.decks.values()):
if m['name'] == name: if self.equalName(m['name'], name):
return m return m
def update(self, g): def update(self, g):
@ -257,7 +257,7 @@ class DeckManager:
def rename(self, g, newName): def rename(self, g, newName):
"Rename deck prefix to NAME if not exists. Updates children." "Rename deck prefix to NAME if not exists. Updates children."
# make sure target node doesn't already exist # make sure target node doesn't already exist
if newName in self.allNames(): if self.byName(newName):
raise DeckRenameError(_("That deck already exists.")) raise DeckRenameError(_("That deck already exists."))
# make sure we're not nesting under a filtered deck # make sure we're not nesting under a filtered deck
for p in self.parentsByName(newName): for p in self.parentsByName(newName):
@ -454,7 +454,7 @@ class DeckManager:
for deck in decks: for deck in decks:
# two decks with the same name? # two decks with the same name?
if deck['name'] in names: if self.normalizeName(deck['name']) in names:
self.col.log("fix duplicate deck name", deck['name']) self.col.log("fix duplicate deck name", deck['name'])
deck['name'] += "%d" % intTime(1000) deck['name'] += "%d" % intTime(1000)
self.save(deck) self.save(deck)
@ -468,12 +468,12 @@ class DeckManager:
# immediate parent must exist # immediate parent must exist
if "::" in deck['name']: if "::" in deck['name']:
immediateParent = "::".join(deck['name'].split("::")[:-1]) immediateParent = "::".join(deck['name'].split("::")[:-1])
if immediateParent not in names: if self.normalizeName(immediateParent) not in names:
self.col.log("fix deck with missing parent", deck['name']) self.col.log("fix deck with missing parent", deck['name'])
self._ensureParents(deck['name']) self._ensureParents(deck['name'])
names.add(immediateParent) names.add(self.normalizeName(immediateParent))
names.add(deck['name']) names.add(self.normalizeName(deck['name']))
def checkIntegrity(self): def checkIntegrity(self):
self._recoverOrphans() self._recoverOrphans()
@ -600,3 +600,11 @@ class DeckManager:
def isDyn(self, did): def isDyn(self, did):
return self.get(did)['dyn'] return self.get(did)['dyn']
@staticmethod
def normalizeName(name):
return unicodedata.normalize("NFC", name.lower())
@staticmethod
def equalName(name1, name2):
return DeckManager.normalizeName(name1) == DeckManager.normalizeName(name2)

View File

@ -1,5 +1,6 @@
# coding: utf-8 # coding: utf-8
from anki.errors import DeckRenameError
from tests.shared import assertException, getEmptyCol from tests.shared import assertException, getEmptyCol
def test_basic(): def test_basic():
@ -87,6 +88,20 @@ def test_rename():
d.decks.rename(d.decks.get(id), "yo") d.decks.rename(d.decks.get(id), "yo")
for n in "yo", "yo::two", "yo::two::three": for n in "yo", "yo::two", "yo::two::three":
assert n in d.decks.allNames() assert n in d.decks.allNames()
# over filtered
filteredId = d.decks.newDyn("filtered")
filtered = d.decks.get(filteredId)
childId = d.decks.id("child")
child = d.decks.get(childId)
assertException(DeckRenameError, lambda: d.decks.rename(child, "filtered::child"))
assertException(DeckRenameError, lambda: d.decks.rename(child, "FILTERED::child"))
# changing case
parentId = d.decks.id("PARENT")
d.decks.id("PARENT::CHILD")
assertException(DeckRenameError, lambda: d.decks.rename(child, "PARENT::CHILD"))
assertException(DeckRenameError, lambda: d.decks.rename(child, "PARENT::child"))
def test_renameForDragAndDrop(): def test_renameForDragAndDrop():
d = getEmptyCol() d = getEmptyCol()
@ -130,6 +145,27 @@ def test_renameForDragAndDrop():
d.decks.renameForDragAndDrop(chinese_did, None) d.decks.renameForDragAndDrop(chinese_did, None)
assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ] assert deckNames() == [ 'Chinese', 'Chinese::HSK', 'Languages' ]
# can't drack a deck where sibling have same name
new_hsk_did = d.decks.id("HSK")
assertException(DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did))
d.decks.rem(new_hsk_did)
# can't drack a deck where sibling have same name different case
new_hsk_did = d.decks.id("hsk")
assertException(DeckRenameError, lambda: d.decks.renameForDragAndDrop(new_hsk_did, chinese_did))
d.decks.rem(new_hsk_did)
# '' is a convenient alias for the top level DID # '' is a convenient alias for the top level DID
d.decks.renameForDragAndDrop(hsk_did, '') d.decks.renameForDragAndDrop(hsk_did, '')
assert deckNames() == [ 'Chinese', 'HSK', 'Languages' ] assert deckNames() == [ 'Chinese', 'HSK', 'Languages' ]
def test_check():
d = getEmptyCol()
foo_did = d.decks.id("foo")
FOO_did = d.decks.id("bar")
FOO = d.decks.byName("bar")
FOO["name"] = "FOO"
d.decks.save(FOO)
d.decks._checkDeckTree()
assert "foo" not in d.decks.allNames() or "FOO" not in d.decks.allNames()