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:
parent
a4d723de3e
commit
b8a7151ed0
@ -94,18 +94,18 @@
|
|||||||
"error": null
|
"error": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **suspended**
|
||||||
|
|
||||||
* **areSuspended**
|
Check if card is suspended by its ID. Returns `true` if suspended, `false` otherwise.
|
||||||
|
|
||||||
Returns an array indicating whether each of the given cards is suspended (in the same order).
|
|
||||||
|
|
||||||
*Sample request*:
|
*Sample request*:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"action": "areSuspended",
|
"action": "suspended",
|
||||||
"version": 6,
|
"version": 6,
|
||||||
"params": {
|
"params": {
|
||||||
"cards": [1483959291685, 1483959293217]
|
"card": 1483959293217
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -113,7 +113,31 @@
|
|||||||
*Sample result*:
|
*Sample result*:
|
||||||
```json
|
```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
|
"error": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -32,9 +32,12 @@ import anki
|
|||||||
import anki.exporting
|
import anki.exporting
|
||||||
import anki.storage
|
import anki.storage
|
||||||
import aqt
|
import aqt
|
||||||
|
from anki.cards import Card
|
||||||
|
|
||||||
from anki.exporting import AnkiPackageExporter
|
from anki.exporting import AnkiPackageExporter
|
||||||
from anki.importing import AnkiPackageImporter
|
from anki.importing import AnkiPackageImporter
|
||||||
|
from anki.notes import Note
|
||||||
|
from anki.rsbackend import NotFoundError
|
||||||
from anki.utils import joinFields, intTime, guid64, fieldChecksum
|
from anki.utils import joinFields, intTime, guid64, fieldChecksum
|
||||||
|
|
||||||
from . import web, util
|
from . import web, util
|
||||||
@ -280,6 +283,17 @@ class AnkiConnect:
|
|||||||
|
|
||||||
return 0
|
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
|
# Miscellaneous
|
||||||
@ -599,9 +613,7 @@ class AnkiConnect:
|
|||||||
|
|
||||||
@util.api()
|
@util.api()
|
||||||
def updateNoteFields(self, note):
|
def updateNoteFields(self, note):
|
||||||
ankiNote = self.collection().getNote(note['id'])
|
ankiNote = self.getNote(note['id'])
|
||||||
if ankiNote is None:
|
|
||||||
raise Exception('note was not found: {}'.format(note['id']))
|
|
||||||
|
|
||||||
for name, value in note['fields'].items():
|
for name, value in note['fields'].items():
|
||||||
if name in ankiNote:
|
if name in ankiNote:
|
||||||
@ -646,10 +658,14 @@ class AnkiConnect:
|
|||||||
self.window().progress.start()
|
self.window().progress.start()
|
||||||
|
|
||||||
for nid in notes:
|
for nid in notes:
|
||||||
note = self.collection().getNote(nid)
|
try:
|
||||||
|
note = self.getNote(nid)
|
||||||
|
except NotFoundError:
|
||||||
|
continue
|
||||||
|
|
||||||
if note.hasTag(tag_to_replace):
|
if note.hasTag(tag_to_replace):
|
||||||
note.delTag(tag_to_replace)
|
note.delTag(tag_to_replace)
|
||||||
note.addtag(replace_with_tag)
|
note.addTag(replace_with_tag)
|
||||||
note.flush()
|
note.flush()
|
||||||
|
|
||||||
self.window().requireReset()
|
self.window().requireReset()
|
||||||
@ -661,11 +677,12 @@ class AnkiConnect:
|
|||||||
def replaceTagsInAllNotes(self, tag_to_replace, replace_with_tag):
|
def replaceTagsInAllNotes(self, tag_to_replace, replace_with_tag):
|
||||||
self.window().progress.start()
|
self.window().progress.start()
|
||||||
|
|
||||||
|
collection = self.collection()
|
||||||
for nid in collection.db.list('select id from notes'):
|
for nid in collection.db.list('select id from notes'):
|
||||||
note = collection.getNote(nid)
|
note = self.getNote(nid)
|
||||||
if note.hasTag(tag_to_replace):
|
if note.hasTag(tag_to_replace):
|
||||||
note.delTag(tag_to_replace)
|
note.delTag(tag_to_replace)
|
||||||
note.addtag(replace_with_tag)
|
note.addTag(replace_with_tag)
|
||||||
note.flush()
|
note.flush()
|
||||||
|
|
||||||
self.window().requireReset()
|
self.window().requireReset()
|
||||||
@ -677,12 +694,13 @@ class AnkiConnect:
|
|||||||
def setEaseFactors(self, cards, easeFactors):
|
def setEaseFactors(self, cards, easeFactors):
|
||||||
couldSetEaseFactors = []
|
couldSetEaseFactors = []
|
||||||
for i, card in enumerate(cards):
|
for i, card in enumerate(cards):
|
||||||
ankiCard = self.collection().getCard(card)
|
try:
|
||||||
if ankiCard is None:
|
ankiCard = self.getCard(card)
|
||||||
|
except NotFoundError:
|
||||||
couldSetEaseFactors.append(False)
|
couldSetEaseFactors.append(False)
|
||||||
else:
|
continue
|
||||||
couldSetEaseFactors.append(True)
|
|
||||||
|
|
||||||
|
couldSetEaseFactors.append(True)
|
||||||
ankiCard.factor = easeFactors[i]
|
ankiCard.factor = easeFactors[i]
|
||||||
ankiCard.flush()
|
ankiCard.flush()
|
||||||
|
|
||||||
@ -693,7 +711,12 @@ class AnkiConnect:
|
|||||||
def getEaseFactors(self, cards):
|
def getEaseFactors(self, cards):
|
||||||
easeFactors = []
|
easeFactors = []
|
||||||
for card in cards:
|
for card in cards:
|
||||||
ankiCard = self.collection().getCard(card)
|
try:
|
||||||
|
ankiCard = self.getCard(card)
|
||||||
|
except NotFoundError:
|
||||||
|
easeFactors.append(None)
|
||||||
|
continue
|
||||||
|
|
||||||
easeFactors.append(ankiCard.factor)
|
easeFactors.append(ankiCard.factor)
|
||||||
|
|
||||||
return easeFactors
|
return easeFactors
|
||||||
@ -726,7 +749,7 @@ class AnkiConnect:
|
|||||||
|
|
||||||
@util.api()
|
@util.api()
|
||||||
def suspended(self, card):
|
def suspended(self, card):
|
||||||
card = self.collection().getCard(card)
|
card = self.getCard(card)
|
||||||
return card.queue == -1
|
return card.queue == -1
|
||||||
|
|
||||||
|
|
||||||
@ -734,7 +757,10 @@ class AnkiConnect:
|
|||||||
def areSuspended(self, cards):
|
def areSuspended(self, cards):
|
||||||
suspended = []
|
suspended = []
|
||||||
for card in cards:
|
for card in cards:
|
||||||
suspended.append(self.suspended(card))
|
try:
|
||||||
|
suspended.append(self.suspended(card))
|
||||||
|
except NotFoundError:
|
||||||
|
suspended.append(None)
|
||||||
|
|
||||||
return suspended
|
return suspended
|
||||||
|
|
||||||
@ -994,7 +1020,7 @@ class AnkiConnect:
|
|||||||
result = []
|
result = []
|
||||||
for cid in cards:
|
for cid in cards:
|
||||||
try:
|
try:
|
||||||
card = self.collection().getCard(cid)
|
card = self.getCard(cid)
|
||||||
model = card.model()
|
model = card.model()
|
||||||
note = card.note()
|
note = card.note()
|
||||||
fields = {}
|
fields = {}
|
||||||
@ -1025,8 +1051,8 @@ class AnkiConnect:
|
|||||||
'lapses': card.lapses,
|
'lapses': card.lapses,
|
||||||
'left': card.left,
|
'left': card.left,
|
||||||
})
|
})
|
||||||
except TypeError as e:
|
except NotFoundError:
|
||||||
# Anki will give a TypeError if the card ID does not exist.
|
# Anki will give a NotFoundError if the card ID does not exist.
|
||||||
# Best behavior is probably to add an 'empty card' to the
|
# Best behavior is probably to add an 'empty card' to the
|
||||||
# returned result, so that the items of the input and return
|
# returned result, so that the items of the input and return
|
||||||
# lists correspond.
|
# lists correspond.
|
||||||
@ -1127,7 +1153,7 @@ class AnkiConnect:
|
|||||||
result = []
|
result = []
|
||||||
for nid in notes:
|
for nid in notes:
|
||||||
try:
|
try:
|
||||||
note = self.collection().getNote(nid)
|
note = self.getNote(nid)
|
||||||
model = note.model()
|
model = note.model()
|
||||||
|
|
||||||
fields = {}
|
fields = {}
|
||||||
@ -1143,8 +1169,8 @@ class AnkiConnect:
|
|||||||
'modelName': model['name'],
|
'modelName': model['name'],
|
||||||
'cards': self.collection().db.list('select id from cards where nid = ? order by ord', note.id)
|
'cards': self.collection().db.list('select id from cards where nid = ? order by ord', note.id)
|
||||||
})
|
})
|
||||||
except TypeError as e:
|
except NotFoundError:
|
||||||
# Anki will give a TypeError if the note ID does not exist.
|
# Anki will give a NotFoundError if the note ID does not exist.
|
||||||
# Best behavior is probably to add an 'empty card' to the
|
# Best behavior is probably to add an 'empty card' to the
|
||||||
# returned result, so that the items of the input and return
|
# returned result, so that the items of the input and return
|
||||||
# lists correspond.
|
# lists correspond.
|
||||||
|
@ -24,6 +24,8 @@ class TestCards(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
incorrectId = 1234
|
||||||
|
|
||||||
# findCards
|
# findCards
|
||||||
cardIds = util.invoke('findCards', query='deck:test')
|
cardIds = util.invoke('findCards', query='deck:test')
|
||||||
self.assertEqual(len(cardIds), 1)
|
self.assertEqual(len(cardIds), 1)
|
||||||
@ -33,18 +35,24 @@ class TestCards(unittest.TestCase):
|
|||||||
easeFactors = [EASE_TO_TRY for card in cardIds]
|
easeFactors = [EASE_TO_TRY for card in cardIds]
|
||||||
couldGetEaseFactors = util.invoke('setEaseFactors', cards=cardIds, easeFactors=easeFactors)
|
couldGetEaseFactors = util.invoke('setEaseFactors', cards=cardIds, easeFactors=easeFactors)
|
||||||
self.assertEqual([True for card in cardIds], couldGetEaseFactors)
|
self.assertEqual([True for card in cardIds], couldGetEaseFactors)
|
||||||
|
couldGetEaseFactors = util.invoke('setEaseFactors', cards=[incorrectId], easeFactors=[EASE_TO_TRY])
|
||||||
|
self.assertEqual([False], couldGetEaseFactors)
|
||||||
|
|
||||||
# getEaseFactors
|
# getEaseFactors
|
||||||
easeFactorsFound = util.invoke('getEaseFactors', cards=cardIds)
|
easeFactorsFound = util.invoke('getEaseFactors', cards=cardIds)
|
||||||
self.assertEqual(easeFactors, easeFactorsFound)
|
self.assertEqual(easeFactors, easeFactorsFound)
|
||||||
|
easeFactorsFound = util.invoke('getEaseFactors', cards=[incorrectId])
|
||||||
|
self.assertEqual([None], easeFactorsFound)
|
||||||
|
|
||||||
# suspend
|
# suspend
|
||||||
util.invoke('suspend', cards=cardIds)
|
util.invoke('suspend', cards=cardIds)
|
||||||
|
self.assertRaises(Exception, lambda: util.invoke('suspend', cards=[incorrectId]))
|
||||||
|
|
||||||
# areSuspended (part 1)
|
# areSuspended (part 1)
|
||||||
suspendedStates = util.invoke('areSuspended', cards=cardIds)
|
suspendedStates = util.invoke('areSuspended', cards=cardIds)
|
||||||
self.assertEqual(len(cardIds), len(suspendedStates))
|
self.assertEqual(len(cardIds), len(suspendedStates))
|
||||||
self.assertNotIn(False, suspendedStates)
|
self.assertNotIn(False, suspendedStates)
|
||||||
|
self.assertEqual([None], util.invoke('areSuspended', cards=[incorrectId]))
|
||||||
|
|
||||||
# unsuspend
|
# unsuspend
|
||||||
util.invoke('unsuspend', cards=cardIds)
|
util.invoke('unsuspend', cards=cardIds)
|
||||||
@ -73,6 +81,9 @@ class TestCards(unittest.TestCase):
|
|||||||
self.assertEqual(len(cardsInfo), len(cardIds))
|
self.assertEqual(len(cardsInfo), len(cardIds))
|
||||||
for i, cardInfo in enumerate(cardsInfo):
|
for i, cardInfo in enumerate(cardsInfo):
|
||||||
self.assertEqual(cardInfo['cardId'], cardIds[i])
|
self.assertEqual(cardInfo['cardId'], cardIds[i])
|
||||||
|
cardsInfo = util.invoke('cardsInfo', cards=[incorrectId])
|
||||||
|
self.assertEqual(len(cardsInfo), 1)
|
||||||
|
self.assertDictEqual(cardsInfo[0], dict())
|
||||||
|
|
||||||
# forgetCards
|
# forgetCards
|
||||||
util.invoke('forgetCards', cards=cardIds)
|
util.invoke('forgetCards', cards=cardIds)
|
||||||
|
@ -91,15 +91,22 @@ class TestNotes(unittest.TestCase):
|
|||||||
util.invoke('removeTags', notes=[noteId], tags='tag2')
|
util.invoke('removeTags', notes=[noteId], tags='tag2')
|
||||||
|
|
||||||
# updateNoteFields
|
# 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'}}
|
noteUpdate = {'id': noteId, 'fields': {'Front': 'front2', 'Back': 'back2'}}
|
||||||
util.invoke('updateNoteFields', note=noteUpdate)
|
util.invoke('updateNoteFields', note=noteUpdate)
|
||||||
|
|
||||||
|
# replaceTags
|
||||||
|
util.invoke('replaceTags', notes=[noteId, incorrectId], tag_to_replace='tag1', replace_with_tag='new_tag')
|
||||||
|
|
||||||
# notesInfo (part 2)
|
# notesInfo (part 2)
|
||||||
noteInfos = util.invoke('notesInfo', notes=[noteId])
|
noteInfos = util.invoke('notesInfo', notes=[noteId, incorrectId])
|
||||||
self.assertEqual(len(noteInfos), 1)
|
self.assertEqual(len(noteInfos), 2)
|
||||||
|
self.assertDictEqual(noteInfos[1], dict()) # Test that returns empty dict if incorrect id was passed
|
||||||
noteInfo = noteInfos[0]
|
noteInfo = noteInfos[0]
|
||||||
self.assertSetEqual(set(noteInfo['tags']), {'tag1'})
|
self.assertSetEqual(set(noteInfo['tags']), {'new_tag'})
|
||||||
self.assertIn('tag1', noteInfo['tags'])
|
self.assertIn('new_tag', noteInfo['tags'])
|
||||||
self.assertNotIn('tag2', noteInfo['tags'])
|
self.assertNotIn('tag2', noteInfo['tags'])
|
||||||
self.assertEqual(noteInfo['fields']['Front']['value'], 'front2')
|
self.assertEqual(noteInfo['fields']['Front']['value'], 'front2')
|
||||||
self.assertEqual(noteInfo['fields']['Back']['value'], 'back2')
|
self.assertEqual(noteInfo['fields']['Back']['value'], 'back2')
|
||||||
@ -115,6 +122,15 @@ class TestNotes(unittest.TestCase):
|
|||||||
for noteId in noteIds:
|
for noteId in noteIds:
|
||||||
self.assertNotEqual(noteId, None)
|
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)
|
# canAddNotes (part 2)
|
||||||
noteStates = util.invoke('canAddNotes', notes=notes2)
|
noteStates = util.invoke('canAddNotes', notes=notes2)
|
||||||
self.assertNotIn(True, noteStates)
|
self.assertNotIn(True, noteStates)
|
||||||
|
Loading…
Reference in New Issue
Block a user