Duplicate scope options (#203)

* Add additional options to addNote's duplicateScope option

* Update documentation
This commit is contained in:
toasted-nutbread 2020-11-01 19:14:30 -05:00 committed by GitHub
parent b19bd7902d
commit ab1f518474
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 11 deletions

View File

@ -11,9 +11,14 @@
downloaded files with an MD5 hash that matches the provided value. This is useful for avoiding the saving of error 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 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. 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 Normally duplicate cards can not be added and trigger exception.
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. 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*: *Sample request*:
```json ```json
@ -30,7 +35,11 @@
}, },
"options": { "options": {
"allowDuplicate": false, "allowDuplicate": false,
"duplicateScope": "deck" "duplicateScope": "deck",
"duplicateScopeOptions": {
"deckName": "Default",
"checkChildren": false
}
}, },
"tags": [ "tags": [
"yomichan" "yomichan"

View File

@ -207,6 +207,8 @@ class AnkiConnect:
allowDuplicate = False allowDuplicate = False
duplicateScope = None duplicateScope = None
duplicateScopeDeckName = None
duplicateScopeCheckChildren = False
if 'options' in note: if 'options' in note:
if 'allowDuplicate' in note['options']: if 'allowDuplicate' in note['options']:
allowDuplicate = note['options']['allowDuplicate'] allowDuplicate = note['options']['allowDuplicate']
@ -214,8 +216,16 @@ class AnkiConnect:
raise Exception('option parameter \'allowDuplicate\' must be boolean') raise Exception('option parameter \'allowDuplicate\' must be boolean')
if 'duplicateScope' in note['options']: if 'duplicateScope' in note['options']:
duplicateScope = note['options']['duplicateScope'] 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: if duplicateOrEmpty == 1:
raise Exception('cannot create note because it is empty') raise Exception('cannot create note because it is empty')
elif duplicateOrEmpty == 2: elif duplicateOrEmpty == 2:
@ -228,7 +238,7 @@ class AnkiConnect:
else: else:
raise Exception('cannot create note for unknown reason') 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." "1 if first is empty; 2 if first is a duplicate, False otherwise."
result = note.dupeOrEmpty() result = note.dupeOrEmpty()
if result != 2 or duplicateScope != 'deck': if result != 2 or duplicateScope != 'deck':
@ -240,17 +250,31 @@ class AnkiConnect:
did = deck['id'] did = deck['id']
csum = anki.utils.fieldChecksum(val) 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( for noteId in note.col.db.list(
"select id from notes where csum = ? and id != ? and mid = ?", "select id from notes where csum = ? and id != ? and mid = ?",
csum, csum,
note.id or 0, note.id or 0,
note.mid, note.mid,
): ):
if note.col.db.scalar( for cardDeckId in note.col.db.list(
"select id from cards where nid = ? and did = ?", "select did from cards where nid = ?",
noteId, noteId
did
): ):
if cardDeckId in dids:
return 2 return 2
return False return False