From 3d6c0ec36c362ef927acc8f55775dfc4dee7b2e4 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 15:49:09 +0100 Subject: [PATCH 1/8] Test: rename over filtered deck --- tests/test_decks.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_decks.py b/tests/test_decks.py index f127688f4..55226501b 100644 --- a/tests/test_decks.py +++ b/tests/test_decks.py @@ -1,5 +1,6 @@ # coding: utf-8 +from anki.errors import DeckRenameError from tests.shared import assertException, getEmptyCol def test_basic(): @@ -87,6 +88,14 @@ def test_rename(): d.decks.rename(d.decks.get(id), "yo") for n in "yo", "yo::two", "yo::two::three": assert n in d.decks.allNames() + # over filtered + parentId = d.decks.newDyn("parent") + parent = d.decks.get(parentId) + childId = d.decks.id("child") + child = d.decks.get(childId) + assertException(DeckRenameError, lambda: d.decks.rename(child, "parent::child")) + assertException(DeckRenameError, lambda: d.decks.rename(child, "PARENT::child")) + def test_renameForDragAndDrop(): d = getEmptyCol() From e44b0492781e8248318e87a47eb179561db94b7b Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 15:26:53 +0100 Subject: [PATCH 2/8] Decks: methods to normalize name and check equality of name --- anki/decks.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/anki/decks.py b/anki/decks.py index d692cfd4d..9c994945f 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -600,3 +600,11 @@ class DeckManager: def isDyn(self, did): 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) From 3afb28c05f210f13b86413d8356ea5ee29f5a7a3 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 15:45:26 +0100 Subject: [PATCH 3/8] DeckManager.byName check equality ignoring case name --- anki/decks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/anki/decks.py b/anki/decks.py index 9c994945f..0a36b4a99 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -242,9 +242,9 @@ class DeckManager: return self.decks['1'] def byName(self, name): - "Get deck with NAME." + """Get deck with NAME, ignoring cases.""" for m in list(self.decks.values()): - if m['name'] == name: + if self.equalName(m['name'], name): return m def update(self, g): From 31ccd31e9722d594ea5d1cb06a18b69cd5c179a4 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 15:48:33 +0100 Subject: [PATCH 4/8] DeckManager.id return existing deck ignoring case --- anki/decks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/anki/decks.py b/anki/decks.py index 0a36b4a99..036014891 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -135,9 +135,9 @@ class DeckManager: type = defaultDeck name = name.replace('"', '') name = unicodedata.normalize("NFC", name) - for id, g in list(self.decks.items()): - if unicodedata.normalize("NFC", g['name'].lower()) == name.lower(): - return int(id) + deck = self.byName(name) + if deck: + return int(deck["id"]) if not create: return None g = copy.deepcopy(type) From 769d52d6b260154c760b9d0fe452a3fd80b6d4b1 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 16:29:54 +0100 Subject: [PATCH 5/8] Test: Renaming with different cases --- tests/test_decks.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tests/test_decks.py b/tests/test_decks.py index 55226501b..76aebc0ea 100644 --- a/tests/test_decks.py +++ b/tests/test_decks.py @@ -89,14 +89,20 @@ def test_rename(): for n in "yo", "yo::two", "yo::two::three": assert n in d.decks.allNames() # over filtered - parentId = d.decks.newDyn("parent") - parent = d.decks.get(parentId) + 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, "parent::child")) + 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(): d = getEmptyCol() @@ -139,6 +145,16 @@ def test_renameForDragAndDrop(): d.decks.renameForDragAndDrop(chinese_did, None) 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 d.decks.renameForDragAndDrop(hsk_did, '') assert deckNames() == [ 'Chinese', 'HSK', 'Languages' ] From 6aacd7782f5d27e8ade3762635795987eadb5f72 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 16:31:29 +0100 Subject: [PATCH 6/8] Rename reject even when another deck has same name different cases --- anki/decks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anki/decks.py b/anki/decks.py index 036014891..8d8a73108 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -257,7 +257,7 @@ class DeckManager: def rename(self, g, newName): "Rename deck prefix to NAME if not exists. Updates children." # make sure target node doesn't already exist - if newName in self.allNames(): + if self.byName(newName): raise DeckRenameError(_("That deck already exists.")) # make sure we're not nesting under a filtered deck for p in self.parentsByName(newName): From 9a40f29bce8e068d8ed70e50830b1c72ba14dd26 Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 17:00:28 +0100 Subject: [PATCH 7/8] test that if two decks differs only by case, they are renamed --- tests/test_decks.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_decks.py b/tests/test_decks.py index 76aebc0ea..f88f335d3 100644 --- a/tests/test_decks.py +++ b/tests/test_decks.py @@ -158,3 +158,14 @@ def test_renameForDragAndDrop(): # '' is a convenient alias for the top level DID d.decks.renameForDragAndDrop(hsk_did, '') 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() From 9955048aecf32ddd1826b5c167cd39efa0977a0c Mon Sep 17 00:00:00 2001 From: Arthur Milchior Date: Wed, 13 Nov 2019 17:02:12 +0100 Subject: [PATCH 8/8] DeckManager: _checkDeckTree ignore case --- anki/decks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/anki/decks.py b/anki/decks.py index 8d8a73108..d07cbd2c9 100644 --- a/anki/decks.py +++ b/anki/decks.py @@ -454,7 +454,7 @@ class DeckManager: for deck in decks: # 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']) deck['name'] += "%d" % intTime(1000) self.save(deck) @@ -468,12 +468,12 @@ class DeckManager: # immediate parent must exist if "::" in deck['name']: 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._ensureParents(deck['name']) - names.add(immediateParent) + names.add(self.normalizeName(immediateParent)) - names.add(deck['name']) + names.add(self.normalizeName(deck['name'])) def checkIntegrity(self): self._recoverOrphans()