diff --git a/actions/cards.md b/actions/cards.md index 3857bb8..c2c323f 100644 --- a/actions/cards.md +++ b/actions/cards.md @@ -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 } ``` diff --git a/plugin/__init__.py b/plugin/__init__.py index def3ba6..0da6d44 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -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. diff --git a/tests/test_cards.py b/tests/test_cards.py index f08be4f..80e82d2 100755 --- a/tests/test_cards.py +++ b/tests/test_cards.py @@ -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) diff --git a/tests/test_notes.py b/tests/test_notes.py index d481550..a87b3b0 100755 --- a/tests/test_notes.py +++ b/tests/test_notes.py @@ -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)