diff --git a/actions/notes.md b/actions/notes.md index 7985b43..f6e8024 100644 --- a/actions/notes.md +++ b/actions/notes.md @@ -11,9 +11,14 @@ downloaded files with an MD5 hash that matches the provided value. This is useful for avoiding the saving of error pages and stub files. The `fields` member is a list of fields that should play audio when the card is displayed in Anki. The `allowDuplicate` member inside `options` group can be set to true to enable adding duplicate cards. - Normally duplicate cards can not be added and trigger exception. The `duplicateScope` member inside `options` can be - used to specify the scope for which duplicates are checked. A value of `"deck"` will only check for duplicates in the - target deck; any other value will check the entire collection. + Normally duplicate cards can not be added and trigger exception. + + The `duplicateScope` member inside `options` can be used to specify the scope for which duplicates are checked. + A value of `"deckName"` will only check for duplicates in the target deck; any other value will check the entire collection. + The `duplicateScopeOptions` object can be used to specify some additional settings. `duplicateScopeOptions.deckName` + will specify which deck to use for checking duplicates in. If undefined or `null`, the target deck will be used. + `duplicateScopeOptions.checkChildren` will change whether or not duplicate cards are checked in child decks; + the default value is `false`. *Sample request*: ```json @@ -30,7 +35,11 @@ }, "options": { "allowDuplicate": false, - "duplicateScope": "deck" + "duplicateScope": "deck", + "duplicateScopeOptions": { + "deckName": "Default", + "checkChildren": false + } }, "tags": [ "yomichan" diff --git a/plugin/__init__.py b/plugin/__init__.py index bb50c41..855da46 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -207,6 +207,8 @@ class AnkiConnect: allowDuplicate = False duplicateScope = None + duplicateScopeDeckName = None + duplicateScopeCheckChildren = False if 'options' in note: if 'allowDuplicate' in note['options']: allowDuplicate = note['options']['allowDuplicate'] @@ -214,8 +216,16 @@ class AnkiConnect: raise Exception('option parameter \'allowDuplicate\' must be boolean') if 'duplicateScope' in note['options']: duplicateScope = note['options']['duplicateScope'] + if 'duplicateScopeOptions' in note['options']: + duplicateScopeOptions = note['options']['duplicateScopeOptions'] + if 'deckName' in duplicateScopeOptions: + duplicateScopeDeckName = duplicateScopeOptions['deckName'] + if 'checkChildren' in duplicateScopeOptions: + duplicateScopeCheckChildren = duplicateScopeOptions['checkChildren'] + if type(duplicateScopeCheckChildren) is not bool: + raise Exception('option parameter \'duplicateScopeOptions.checkChildren\' must be boolean') - duplicateOrEmpty = self.isNoteDuplicateOrEmptyInScope(ankiNote, deck, duplicateScope) + duplicateOrEmpty = self.isNoteDuplicateOrEmptyInScope(ankiNote, deck, collection, duplicateScope, duplicateScopeDeckName, duplicateScopeCheckChildren) if duplicateOrEmpty == 1: raise Exception('cannot create note because it is empty') elif duplicateOrEmpty == 2: @@ -228,7 +238,7 @@ class AnkiConnect: else: raise Exception('cannot create note for unknown reason') - def isNoteDuplicateOrEmptyInScope(self, note, deck, duplicateScope): + def isNoteDuplicateOrEmptyInScope(self, note, deck, collection, duplicateScope, duplicateScopeDeckName, duplicateScopeCheckChildren): "1 if first is empty; 2 if first is a duplicate, False otherwise." result = note.dupeOrEmpty() if result != 2 or duplicateScope != 'deck': @@ -240,18 +250,32 @@ class AnkiConnect: did = deck['id'] csum = anki.utils.fieldChecksum(val) + if duplicateScopeDeckName is not None: + deck2 = collection.decks.byName(duplicateScopeDeckName) + if deck2 is None: + # Invalid deck, so cannot be duplicate + return False + did = deck2['id'] + + dids = {} + if duplicateScopeCheckChildren: + for kv in collection.decks.children(did): + dids[kv[1]] = True + else: + dids[did] = True + for noteId in note.col.db.list( "select id from notes where csum = ? and id != ? and mid = ?", csum, note.id or 0, note.mid, ): - if note.col.db.scalar( - "select id from cards where nid = ? and did = ?", - noteId, - did + for cardDeckId in note.col.db.list( + "select did from cards where nid = ?", + noteId ): - return 2 + if cardDeckId in dids: + return 2 return False