More create note duplicate options (#269)
* Simplify access to note['options'] * Add checkAllModels option * Update documentation
This commit is contained in:
parent
591ac06aa3
commit
2150940c7e
15
README.md
15
README.md
@ -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": [
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user