From 7de12f7d4b14acba6d989360eaef9b75efc9d571 Mon Sep 17 00:00:00 2001 From: Jingtao Xu Date: Mon, 1 Jan 2018 10:34:38 +0800 Subject: [PATCH 01/10] add support to modify fields of exist note. --- AnkiConnect.py | 15 +++++++++++++++ README.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/AnkiConnect.py b/AnkiConnect.py index 436c771..56465d4 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -424,6 +424,18 @@ class AnkiBridge: if not note.dupeOrEmpty(): return note + def updateNoteFields(self, params): + collection = self.collection() + if collection is None: + return + + note = collection.getNote(params['id']) + if note is None: + raise Exception("Failed to get note:{}".format(params['id'])) + for name, value in params['fields'].items(): + if name in note: + note[name] = value + note.flush() def addTags(self, notes, tags, add=True): self.startEditing() @@ -1023,6 +1035,9 @@ class AnkiConnect: return results + @webApi() + def updateNoteFields(self, note): + return self.anki.updateNoteFields(note) @webApi() def canAddNotes(self, notes): diff --git a/README.md b/README.md index 900b6b0..6e1c676 100644 --- a/README.md +++ b/README.md @@ -763,6 +763,38 @@ guarantee that your application continues to function properly in the future. } ``` +#### Note Modification #### + +* **updateNoteFields** + + Modify the fields of an exist note. + + *Sample request*: + ```json + { + "action": "updateNoteFields", + "version": 5, + "params": { + "note": { + "id": 1514547547030, + "fields": { + "Front": "new front content", + "Back": "new back content" + }, + } + } + } + ``` + + *Sample result*: + ```json + { + "result": null, + "error": null + } + ``` + + #### Note Tags #### * **addTags** From 63c2eb4887b42a18da6a2e68844c8e92d4a83bbf Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Sat, 6 Jan 2018 20:48:25 +0100 Subject: [PATCH 02/10] Added API endpoint to retrieve card/note information. --- AnkiConnect.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/AnkiConnect.py b/AnkiConnect.py index 56465d4..8c752fe 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -29,6 +29,7 @@ import socket import sys from time import time from unicodedata import normalize +from operator import itemgetter # @@ -706,6 +707,80 @@ class AnkiBridge: else: return [] + def cardsInfo(self,cards): + result = [] + for cid in cards: + try: + card = self.collection().getCard(cid) + model = card.model() + note = card.note() + fields = {} + for info in model['flds']: + order = info['ord'] + name = info['name'] + fields[name] = note.fields[order] + + result.append({ + 'cardId': card.id, + 'fields': fields, + 'fieldOrder': card.ord, + 'question': card._getQA()['q'], + 'answer': card._getQA()['a'], + 'modelName': model['name'], + 'deckName': self.deckNameFromId(card.did), + 'css': model['css'], + 'factor': card.factor, + #This factor is 10 times the ease percentage, + # so an ease of 310% would be reported as 3100 + 'interval': card.ivl, + 'type': card.type, + #new = 0, learning = 1, review = 2 + 'queue': card.queue, + # same as type, plus: + # suspended = -1, user buried = -2, sched buried = -3 + 'due': card.due, + # does different things depending on queue, + # new: position in queue + # learning: integer timestamp + # review: integer days (since first review) + 'note': card.nid + }) + except TypeError as e: + # Anki will give a TypeError 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. + result.append({}) + + return result + + def notesInfo(self,notes): + result = [] + for nid in notes: + try: + note = self.collection().getNote(nid) + model = note.model() + + fields = {} + for info in model['flds']: + order = info['ord'] + name = info['name'] + fields[name] = note.fields[order] + + result.append({ + 'noteId': note.id, + 'tags' : note.tags, + 'fields': fields, + 'modelName': model['name'], + }) + except TypeError as e: + # Anki will give a TypeError 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. + result.append({}) + return result + def getDecks(self, cards): decks = {} @@ -1196,6 +1271,13 @@ class AnkiConnect: def guiExitAnki(self): return self.anki.guiExitAnki() + @webApi() + def cardsInfo(self, cards): + return self.anki.cardsInfo(cards) + + @webApi() + def notesInfo(self, notes): + return self.anki.notesInfo(notes) # # Entry From cd6dd8479c381d15ef18fe6f55d81360c01fe98a Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 14:09:38 +0100 Subject: [PATCH 03/10] Make note/card endpoints more like existing endpoints, removing some Anki-internal fields from it card.type, card.queue and card.due seem difficult to use to me, maybe card due date should be introduced when it's represented in a more meaningful way (like an actual date instead of an integer day). --- AnkiConnect.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/AnkiConnect.py b/AnkiConnect.py index 8c752fe..8997f9a 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -718,7 +718,7 @@ class AnkiBridge: for info in model['flds']: order = info['ord'] name = info['name'] - fields[name] = note.fields[order] + fields[name] = {'value': note.fields[order], 'order': order} result.append({ 'cardId': card.id, @@ -733,16 +733,6 @@ class AnkiBridge: #This factor is 10 times the ease percentage, # so an ease of 310% would be reported as 3100 'interval': card.ivl, - 'type': card.type, - #new = 0, learning = 1, review = 2 - 'queue': card.queue, - # same as type, plus: - # suspended = -1, user buried = -2, sched buried = -3 - 'due': card.due, - # does different things depending on queue, - # new: position in queue - # learning: integer timestamp - # review: integer days (since first review) 'note': card.nid }) except TypeError as e: @@ -765,13 +755,14 @@ class AnkiBridge: for info in model['flds']: order = info['ord'] name = info['name'] - fields[name] = note.fields[order] + fields[name] = {'value': note.fields[order], 'order': order} result.append({ 'noteId': note.id, 'tags' : note.tags, 'fields': fields, 'modelName': model['name'], + 'cards': note.cards() }) except TypeError as e: # Anki will give a TypeError if the note ID does not exist. From 2d051c67c1196cae31e797f452022bf13534f6c1 Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 14:24:56 +0100 Subject: [PATCH 04/10] Fixed notesInfo fetching card IDs belonging to note --- AnkiConnect.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AnkiConnect.py b/AnkiConnect.py index 8997f9a..e051c16 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -762,7 +762,8 @@ class AnkiBridge: 'tags' : note.tags, 'fields': fields, 'modelName': model['name'], - 'cards': note.cards() + 'cards': self.collection().db.list( + "select id from cards where nid = ? order by ord", self.id) }) except TypeError as e: # Anki will give a TypeError if the note ID does not exist. From 150c49d273889554782b4ac17e8fff09a757b1da Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 18:40:07 +0100 Subject: [PATCH 05/10] Updated README to include notesInfo and cardsInfo --- README.md | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/README.md b/README.md index 6e1c676..3b45ecf 100644 --- a/README.md +++ b/README.md @@ -1065,6 +1065,92 @@ guarantee that your application continues to function properly in the future. } ``` +* **cardsInfo** + + Returns a list of objects containing for each card ID the card fields, front and back sides including CSS, note type, the note that the card belongs to, and deck name, as well as ease and interval. + + *Sample request*: + ```json + { + "action": "cardsInfo", + "version": 5, + "params": { + "cards": [1498938915662, 1502098034048] + } + } + ``` + + *Sample result*: + ```json + { + "result": [ + { + "answer": "back content", + "question": "front content", + "deckName": "Default", + "modelName": "Basic", + "fieldOrder": 1, + "fields": { + "Front": {"value": "front content", "order": 0}, + "Back": {"value": "back content", "order": 1} + }, + "css":"p {font-family:Arial;}", + "cardId": 1498938915662, + "interval": 16 + "note":1502298033753 + }, + { + "answer": "back content", + "question": "front content", + "deckName": "Default", + "modelName": "Basic", + "fieldOrder": 0, + "fields": { + "Front": {"value": "front content", "order": 0}, + "Back": {"value": "back content", "order": 1} + }, + "css":"p {font-family:Arial;}", + "cardId": 1502098034048, + "interval": 23 + "note":1502298033753 + } + ], + "error": null + } + ``` + +* **notesInfo** + + Returns a list of objects containing for each note ID the note fields, tags, note type and the cards belonging to the note. + + *Sample request*: + ```json + { + "action": "notesInfo", + "version": 5, + "params": { + } + } + ``` + + *Sample result*: + ```json + { + "result": [ + { + "noteId":1502298033753, + "modelName": "Basic", + "tags":['tag','another_tag'] + "fields": { + "Front": {"value": "front content", "order": 0}, + "Back": {"value": "back content", "order": 1} + }, + } + ], + "error": null + } + ``` + #### Media File Storage #### * **storeMediaFile** From 4411c4969f92cd54dc16398764146a6182d55981 Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 18:40:44 +0100 Subject: [PATCH 06/10] Fixed Typo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3b45ecf..42f87f4 100644 --- a/README.md +++ b/README.md @@ -1129,6 +1129,7 @@ guarantee that your application continues to function properly in the future. "action": "notesInfo", "version": 5, "params": { + "notes": [1502298033753] } } ``` From 6d7b525bad2ffac70adec219ec60b3761e102924 Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 18:44:50 +0100 Subject: [PATCH 07/10] Fixed another typo --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42f87f4..489dd82 100644 --- a/README.md +++ b/README.md @@ -1096,7 +1096,7 @@ guarantee that your application continues to function properly in the future. }, "css":"p {font-family:Arial;}", "cardId": 1498938915662, - "interval": 16 + "interval": 16, "note":1502298033753 }, { @@ -1111,7 +1111,7 @@ guarantee that your application continues to function properly in the future. }, "css":"p {font-family:Arial;}", "cardId": 1502098034048, - "interval": 23 + "interval": 23, "note":1502298033753 } ], @@ -1141,7 +1141,6 @@ guarantee that your application continues to function properly in the future. { "noteId":1502298033753, "modelName": "Basic", - "tags":['tag','another_tag'] "fields": { "Front": {"value": "front content", "order": 0}, "Back": {"value": "back content", "order": 1} From 05b4e9ab8ddb184a4e5542d00ce6df1e9f6dfc4c Mon Sep 17 00:00:00 2001 From: ReneBrals Date: Tue, 9 Jan 2018 18:46:23 +0100 Subject: [PATCH 08/10] Missed a typo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 489dd82..1634948 100644 --- a/README.md +++ b/README.md @@ -1141,6 +1141,7 @@ guarantee that your application continues to function properly in the future. { "noteId":1502298033753, "modelName": "Basic", + "tags":["tag","another_tag"], "fields": { "Front": {"value": "front content", "order": 0}, "Back": {"value": "back content", "order": 1} From 4c96cc49374a2fbb56439c3d3078279000178238 Mon Sep 17 00:00:00 2001 From: louietan Date: Sun, 14 Jan 2018 17:59:17 +0800 Subject: [PATCH 09/10] Fix issue #64 --- AnkiConnect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AnkiConnect.py b/AnkiConnect.py index 556e10c..82a69f2 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -764,7 +764,7 @@ class AnkiBridge: 'fields': fields, 'modelName': model['name'], 'cards': self.collection().db.list( - "select id from cards where nid = ? order by ord", self.id) + "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. From 88b375d4a891565390cc66d2a044cc89f87d7f05 Mon Sep 17 00:00:00 2001 From: louie Date: Sun, 14 Jan 2018 19:26:37 +0800 Subject: [PATCH 10/10] Add getTags action --- AnkiConnect.py | 9 +++++++++ README.md | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/AnkiConnect.py b/AnkiConnect.py index 556e10c..02959a3 100644 --- a/AnkiConnect.py +++ b/AnkiConnect.py @@ -445,6 +445,10 @@ class AnkiBridge: self.stopEditing() + def getTags(self): + return self.collection().tags.all() + + def suspend(self, cards, suspend=True): for card in cards: isSuspended = self.isSuspended(card) @@ -1127,6 +1131,11 @@ class AnkiConnect: return self.anki.addTags(notes, tags, False) + @webApi() + def getTags(self): + return self.anki.getTags() + + @webApi() def suspend(self, cards, suspend=True): return self.anki.suspend(cards, suspend) diff --git a/README.md b/README.md index 1634948..3fd4945 100644 --- a/README.md +++ b/README.md @@ -845,6 +845,26 @@ guarantee that your application continues to function properly in the future. } ``` +* **getTags** + + Gets the complete list of tags for the current user. + + *Sample request*: + ```json + { + "action": "getTags", + "version": 5 + } + ``` + + *Sample result*: + ```json + { + "result": ["european-languages", "idioms"], + "error": null + } + ``` + #### Card Suspension #### * **suspend**