Fix handling of errors in several methods (#238)

* Fix error message in updateNoteFields method

* Fix error handling in cardsInfo, notesInfo methods

* Add more tests for updateNoteFields, notesInfo

* Fix error handling in several methods

Also fixed obvious bugs in replaceTags and replaceTagsInAllNotes
methods.

* Add more tests for cards methods

* Add more tests for notes methods

* Update documentation on cards methods
This commit is contained in:
Kirill Salnikov 2021-03-05 07:06:25 +03:00 committed by GitHub
parent a4d723de3e
commit b8a7151ed0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 31 deletions

View File

@ -94,18 +94,18 @@
"error": null
}
```
* **suspended**
* **areSuspended**
Returns an array indicating whether each of the given cards is suspended (in the same order).
Check if card is suspended by its ID. Returns `true` if suspended, `false` otherwise.
*Sample request*:
```json
{
"action": "areSuspended",
"action": "suspended",
"version": 6,
"params": {
"cards": [1483959291685, 1483959293217]
"card": 1483959293217
}
}
```
@ -113,7 +113,31 @@
*Sample result*:
```json
{
"result": [false, true],
"result": true,
"error": null
}
```
* **areSuspended**
Returns an array indicating whether each of the given cards is suspended (in the same order). If card doesn't
exist returns `null`.
*Sample request*:
```json
{
"action": "areSuspended",
"version": 6,
"params": {
"cards": [1483959291685, 1483959293217, 1234567891234]
}
}
```
*Sample result*:
```json
{
"result": [false, true, null],
"error": null
}
```

View File

@ -32,9 +32,12 @@ import anki
import anki.exporting
import anki.storage
import aqt
from anki.cards import Card
from anki.exporting import AnkiPackageExporter
from anki.importing import AnkiPackageImporter
from anki.notes import Note
from anki.rsbackend import NotFoundError
from anki.utils import joinFields, intTime, guid64, fieldChecksum
from . import web, util
@ -280,6 +283,17 @@ class AnkiConnect:
return 0
def getCard(self, card_id: int) -> Card:
try:
return self.collection().getCard(card_id)
except NotFoundError:
raise NotFoundError('Card was not found: {}'.format(card_id))
def getNote(self, note_id: int) -> Note:
try:
return self.collection().getNote(note_id)
except NotFoundError:
raise NotFoundError('Note was not found: {}'.format(note_id))
#
# Miscellaneous
@ -599,9 +613,7 @@ class AnkiConnect:
@util.api()
def updateNoteFields(self, note):
ankiNote = self.collection().getNote(note['id'])
if ankiNote is None:
raise Exception('note was not found: {}'.format(note['id']))
ankiNote = self.getNote(note['id'])
for name, value in note['fields'].items():
if name in ankiNote:
@ -646,10 +658,14 @@ class AnkiConnect:
self.window().progress.start()
for nid in notes:
note = self.collection().getNote(nid)
try:
note = self.getNote(nid)
except NotFoundError:
continue
if note.hasTag(tag_to_replace):
note.delTag(tag_to_replace)
note.addtag(replace_with_tag)
note.addTag(replace_with_tag)
note.flush()
self.window().requireReset()
@ -661,11 +677,12 @@ class AnkiConnect:
def replaceTagsInAllNotes(self, tag_to_replace, replace_with_tag):
self.window().progress.start()
collection = self.collection()
for nid in collection.db.list('select id from notes'):
note = collection.getNote(nid)
note = self.getNote(nid)
if note.hasTag(tag_to_replace):
note.delTag(tag_to_replace)
note.addtag(replace_with_tag)
note.addTag(replace_with_tag)
note.flush()
self.window().requireReset()
@ -677,12 +694,13 @@ class AnkiConnect:
def setEaseFactors(self, cards, easeFactors):
couldSetEaseFactors = []
for i, card in enumerate(cards):
ankiCard = self.collection().getCard(card)
if ankiCard is None:
try:
ankiCard = self.getCard(card)
except NotFoundError:
couldSetEaseFactors.append(False)
else:
couldSetEaseFactors.append(True)
continue
couldSetEaseFactors.append(True)
ankiCard.factor = easeFactors[i]
ankiCard.flush()
@ -693,7 +711,12 @@ class AnkiConnect:
def getEaseFactors(self, cards):
easeFactors = []
for card in cards:
ankiCard = self.collection().getCard(card)
try:
ankiCard = self.getCard(card)
except NotFoundError:
easeFactors.append(None)
continue
easeFactors.append(ankiCard.factor)
return easeFactors
@ -726,7 +749,7 @@ class AnkiConnect:
@util.api()
def suspended(self, card):
card = self.collection().getCard(card)
card = self.getCard(card)
return card.queue == -1
@ -734,7 +757,10 @@ class AnkiConnect:
def areSuspended(self, cards):
suspended = []
for card in cards:
suspended.append(self.suspended(card))
try:
suspended.append(self.suspended(card))
except NotFoundError:
suspended.append(None)
return suspended
@ -994,7 +1020,7 @@ class AnkiConnect:
result = []
for cid in cards:
try:
card = self.collection().getCard(cid)
card = self.getCard(cid)
model = card.model()
note = card.note()
fields = {}
@ -1025,8 +1051,8 @@ class AnkiConnect:
'lapses': card.lapses,
'left': card.left,
})
except TypeError as e:
# Anki will give a TypeError if the card ID does not exist.
except NotFoundError:
# Anki will give a NotFoundError if the card ID does not exist.
# Best behavior is probably to add an 'empty card' to the
# returned result, so that the items of the input and return
# lists correspond.
@ -1127,7 +1153,7 @@ class AnkiConnect:
result = []
for nid in notes:
try:
note = self.collection().getNote(nid)
note = self.getNote(nid)
model = note.model()
fields = {}
@ -1143,8 +1169,8 @@ class AnkiConnect:
'modelName': model['name'],
'cards': self.collection().db.list('select id from cards where nid = ? order by ord', note.id)
})
except TypeError as e:
# Anki will give a TypeError if the note ID does not exist.
except NotFoundError:
# Anki will give a NotFoundError if the note ID does not exist.
# Best behavior is probably to add an 'empty card' to the
# returned result, so that the items of the input and return
# lists correspond.

View File

@ -24,6 +24,8 @@ class TestCards(unittest.TestCase):
def runTest(self):
incorrectId = 1234
# findCards
cardIds = util.invoke('findCards', query='deck:test')
self.assertEqual(len(cardIds), 1)
@ -33,18 +35,24 @@ class TestCards(unittest.TestCase):
easeFactors = [EASE_TO_TRY for card in cardIds]
couldGetEaseFactors = util.invoke('setEaseFactors', cards=cardIds, easeFactors=easeFactors)
self.assertEqual([True for card in cardIds], couldGetEaseFactors)
couldGetEaseFactors = util.invoke('setEaseFactors', cards=[incorrectId], easeFactors=[EASE_TO_TRY])
self.assertEqual([False], couldGetEaseFactors)
# getEaseFactors
easeFactorsFound = util.invoke('getEaseFactors', cards=cardIds)
self.assertEqual(easeFactors, easeFactorsFound)
easeFactorsFound = util.invoke('getEaseFactors', cards=[incorrectId])
self.assertEqual([None], easeFactorsFound)
# suspend
util.invoke('suspend', cards=cardIds)
self.assertRaises(Exception, lambda: util.invoke('suspend', cards=[incorrectId]))
# areSuspended (part 1)
suspendedStates = util.invoke('areSuspended', cards=cardIds)
self.assertEqual(len(cardIds), len(suspendedStates))
self.assertNotIn(False, suspendedStates)
self.assertEqual([None], util.invoke('areSuspended', cards=[incorrectId]))
# unsuspend
util.invoke('unsuspend', cards=cardIds)
@ -73,6 +81,9 @@ class TestCards(unittest.TestCase):
self.assertEqual(len(cardsInfo), len(cardIds))
for i, cardInfo in enumerate(cardsInfo):
self.assertEqual(cardInfo['cardId'], cardIds[i])
cardsInfo = util.invoke('cardsInfo', cards=[incorrectId])
self.assertEqual(len(cardsInfo), 1)
self.assertDictEqual(cardsInfo[0], dict())
# forgetCards
util.invoke('forgetCards', cards=cardIds)

View File

@ -91,15 +91,22 @@ class TestNotes(unittest.TestCase):
util.invoke('removeTags', notes=[noteId], tags='tag2')
# updateNoteFields
incorrectId = 1234
noteUpdateIncorrectId = {'id': incorrectId, 'fields': {'Front': 'front2', 'Back': 'back2'}}
self.assertRaises(Exception, lambda: util.invoke('updateNoteFields', note=noteUpdateIncorrectId))
noteUpdate = {'id': noteId, 'fields': {'Front': 'front2', 'Back': 'back2'}}
util.invoke('updateNoteFields', note=noteUpdate)
# replaceTags
util.invoke('replaceTags', notes=[noteId, incorrectId], tag_to_replace='tag1', replace_with_tag='new_tag')
# notesInfo (part 2)
noteInfos = util.invoke('notesInfo', notes=[noteId])
self.assertEqual(len(noteInfos), 1)
noteInfos = util.invoke('notesInfo', notes=[noteId, incorrectId])
self.assertEqual(len(noteInfos), 2)
self.assertDictEqual(noteInfos[1], dict()) # Test that returns empty dict if incorrect id was passed
noteInfo = noteInfos[0]
self.assertSetEqual(set(noteInfo['tags']), {'tag1'})
self.assertIn('tag1', noteInfo['tags'])
self.assertSetEqual(set(noteInfo['tags']), {'new_tag'})
self.assertIn('new_tag', noteInfo['tags'])
self.assertNotIn('tag2', noteInfo['tags'])
self.assertEqual(noteInfo['fields']['Front']['value'], 'front2')
self.assertEqual(noteInfo['fields']['Back']['value'], 'back2')
@ -115,6 +122,15 @@ class TestNotes(unittest.TestCase):
for noteId in noteIds:
self.assertNotEqual(noteId, None)
# replaceTagsInAllNotes
currentTag = notes1[0]['tags'][0]
new_tag = 'new_tag'
util.invoke('replaceTagsInAllNotes', tag_to_replace=currentTag, replace_with_tag=new_tag)
noteInfos = util.invoke('notesInfo', notes=noteIds)
for noteInfo in noteInfos:
self.assertIn(new_tag, noteInfo['tags'])
self.assertNotIn(currentTag, noteInfo['tags'])
# canAddNotes (part 2)
noteStates = util.invoke('canAddNotes', notes=notes2)
self.assertNotIn(True, noteStates)