More create note duplicate options (#269)

* Simplify access to note['options']

* Add checkAllModels option

* Update documentation
This commit is contained in:
toasted-nutbread 2021-07-12 22:46:22 -04:00 committed by GitHub
parent 591ac06aa3
commit 2150940c7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 34 deletions

View File

@ -1957,11 +1957,13 @@ corresponding to when the API was available for use.
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`.
A value of `"deck"` 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`.
* `duplicateScopeOptions.checkAllModels` specifies whether duplicate checks are performed across all note types. The default value is `false`.
*Sample request*:
```json
@ -1981,7 +1983,8 @@ corresponding to when the API was available for use.
"duplicateScope": "deck",
"duplicateScopeOptions": {
"deckName": "Default",
"checkChildren": false
"checkChildren": false,
"checkAllModels": false
}
},
"tags": [

View File

@ -222,22 +222,28 @@ class AnkiConnect:
duplicateScope = None
duplicateScopeDeckName = None
duplicateScopeCheckChildren = False
duplicateScopeCheckAllModels = False
if 'options' in note:
if 'allowDuplicate' in note['options']:
allowDuplicate = note['options']['allowDuplicate']
options = note['options']
if 'allowDuplicate' in options:
allowDuplicate = options['allowDuplicate']
if type(allowDuplicate) is not bool:
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 'duplicateScope' in options:
duplicateScope = options['duplicateScope']
if 'duplicateScopeOptions' in options:
duplicateScopeOptions = 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')
if 'checkAllModels' in duplicateScopeOptions:
duplicateScopeCheckAllModels = duplicateScopeOptions['checkAllModels']
if type(duplicateScopeCheckAllModels) is not bool:
raise Exception('option parameter "duplicateScopeOptions.checkAllModels" must be boolean')
duplicateOrEmpty = self.isNoteDuplicateOrEmptyInScope(
ankiNote,
@ -245,7 +251,8 @@ class AnkiConnect:
collection,
duplicateScope,
duplicateScopeDeckName,
duplicateScopeCheckChildren
duplicateScopeCheckChildren,
duplicateScopeCheckAllModels
)
if duplicateOrEmpty == 1:
@ -260,36 +267,67 @@ class AnkiConnect:
raise Exception('cannot create note for unknown reason')
def isNoteDuplicateOrEmptyInScope(self, note, deck, collection, duplicateScope, duplicateScopeDeckName, duplicateScopeCheckChildren):
# 1 if first is empty; 2 if first is a duplicate, 0 otherwise.
if duplicateScope != 'deck':
def isNoteDuplicateOrEmptyInScope(
self,
note,
deck,
collection,
duplicateScope,
duplicateScopeDeckName,
duplicateScopeCheckChildren,
duplicateScopeCheckAllModels
):
# Returns: 1 if first is empty, 2 if first is a duplicate, 0 otherwise.
# note.dupeOrEmpty returns if a note is a global duplicate with the specific model.
# This is used as the default check, and the rest of this function is manually
# checking if the note is a duplicate with additional options.
if duplicateScope != 'deck' and not duplicateScopeCheckAllModels:
return note.dupeOrEmpty() or 0
# dupeOrEmpty returns if a note is a global duplicate
# the rest of the function checks to see if the note is a duplicate in the deck
# Primary field for uniqueness
val = note.fields[0]
if not val.strip():
return 1
did = deck['id']
if duplicateScopeDeckName is not None:
deck2 = collection.decks.byName(duplicateScopeDeckName)
if deck2 is None:
# Invalid deck, so cannot be duplicate
return 0
did = deck2['id']
dids = {did: True}
if duplicateScopeCheckChildren:
for kv in collection.decks.children(did):
dids[kv[1]] = True
csum = anki.utils.fieldChecksum(val)
for noteId in note.col.db.list('select id from notes where csum = ? and id != ? and mid = ?', csum, note.id or 0, note.mid):
for cardDeckId in note.col.db.list('select did from cards where nid = ?', noteId):
# Create dictionary of deck ids
dids = None
if duplicateScope == 'deck':
did = deck['id']
if duplicateScopeDeckName is not None:
deck2 = collection.decks.byName(duplicateScopeDeckName)
if deck2 is None:
# Invalid deck, so cannot be duplicate
return 0
did = deck2['id']
dids = {did: True}
if duplicateScopeCheckChildren:
for kv in collection.decks.children(did):
dids[kv[1]] = True
# Build query
query = 'select id from notes where csum=?'
queryArgs = [csum]
if note.id:
query += ' and id!=?'
queryArgs.append(note.id)
if not duplicateScopeCheckAllModels:
query += ' and mid=?'
queryArgs.append(note.mid)
# Search
for noteId in note.col.db.list(query, *queryArgs):
if dids is None:
# Duplicate note exists in the collection
return 2
# Validate that a card exists in one of the specified decks
for cardDeckId in note.col.db.list('select did from cards where nid=?', noteId):
if cardDeckId in dids:
return 2
# Not a duplicate
return 0
def getCard(self, card_id: int) -> Card: