bump version, improvements to api
This commit is contained in:
parent
8f9b69ede6
commit
337261245b
172
AnkiConnect.py
172
AnkiConnect.py
@ -35,7 +35,7 @@ from unicodedata import normalize
|
|||||||
# Constants
|
# Constants
|
||||||
#
|
#
|
||||||
|
|
||||||
API_VERSION = 4
|
API_VERSION = 5
|
||||||
TICK_INTERVAL = 25
|
TICK_INTERVAL = 25
|
||||||
URL_TIMEOUT = 10
|
URL_TIMEOUT = 10
|
||||||
URL_UPGRADE = 'https://raw.githubusercontent.com/FooSoft/anki-connect/master/AnkiConnect.py'
|
URL_UPGRADE = 'https://raw.githubusercontent.com/FooSoft/anki-connect/master/AnkiConnect.py'
|
||||||
@ -68,21 +68,15 @@ else:
|
|||||||
# Helpers
|
# Helpers
|
||||||
#
|
#
|
||||||
|
|
||||||
def webapi(*versions):
|
def webApi(*versions):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
def method(*args, **kwargs):
|
method = lambda *args, **kwargs: func(*args, **kwargs)
|
||||||
return func(*args, **kwargs)
|
|
||||||
setattr(method, 'versions', versions)
|
setattr(method, 'versions', versions)
|
||||||
setattr(method, 'api', True)
|
setattr(method, 'api', True)
|
||||||
return method
|
return method
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def webApi(func):
|
|
||||||
func.webApi = True
|
|
||||||
return func
|
|
||||||
|
|
||||||
|
|
||||||
def makeBytes(data):
|
def makeBytes(data):
|
||||||
return data.encode('utf-8')
|
return data.encode('utf-8')
|
||||||
|
|
||||||
@ -896,130 +890,127 @@ class AnkiConnect:
|
|||||||
|
|
||||||
|
|
||||||
def handler(self, request):
|
def handler(self, request):
|
||||||
action = request.get('action', '')
|
name = request.get('action', '')
|
||||||
if hasattr(self, action):
|
version = request.get('version', 4)
|
||||||
handler = getattr(self, action)
|
|
||||||
if callable(handler) and hasattr(handler, 'webApi') and getattr(handler, 'webApi'):
|
|
||||||
spec = inspect.getargspec(handler)
|
|
||||||
argsAll = spec.args[1:]
|
|
||||||
argsReq = argsAll
|
|
||||||
|
|
||||||
argsDef = spec.defaults
|
|
||||||
if argsDef is not None:
|
|
||||||
argsReq = argsAll[:-len(argsDef)]
|
|
||||||
|
|
||||||
params = request.get('params', {})
|
params = request.get('params', {})
|
||||||
for argReq in argsReq:
|
reply = {'result': None, 'error': None}
|
||||||
if argReq not in params:
|
|
||||||
return
|
|
||||||
for param in params:
|
|
||||||
if param not in argsAll:
|
|
||||||
return
|
|
||||||
|
|
||||||
return handler(**params)
|
try:
|
||||||
|
method = None
|
||||||
|
|
||||||
|
for methodName, methodInst in inspect.getmembers(self, predicate=inspect.ismethod):
|
||||||
|
apiVersionLast = 0
|
||||||
|
apiNameLast = None
|
||||||
|
|
||||||
|
if getattr(methodInst, 'api', False):
|
||||||
|
for apiVersion, apiName in getattr(methodInst, 'versions', []):
|
||||||
|
if apiVersionLast < apiVersion <= version:
|
||||||
|
apiVersionLast = apiVersion
|
||||||
|
apiNameLast = apiName
|
||||||
|
|
||||||
|
if apiNameLast is None and apiVersionLast == 0:
|
||||||
|
apiNameLast = methodName
|
||||||
|
|
||||||
|
if apiNameLast is not None and apiNameLast == name:
|
||||||
|
method = methodInst
|
||||||
|
break
|
||||||
|
|
||||||
|
if method is None:
|
||||||
|
raise Exception('unsupported action')
|
||||||
|
else:
|
||||||
|
reply['result'] = methodInst(**params)
|
||||||
|
except Exception as e:
|
||||||
|
reply['error'] = str(e)
|
||||||
|
|
||||||
|
if version > 4:
|
||||||
|
return reply
|
||||||
|
else:
|
||||||
|
return reply['result']
|
||||||
|
|
||||||
|
|
||||||
def invoke(self, version, name, *args, **kwargs):
|
@webApi()
|
||||||
for method_name, method_body in inspect.getmembers(self, predicate=inspect.ismethod):
|
|
||||||
api_version_last = 0
|
|
||||||
api_name_last = None
|
|
||||||
|
|
||||||
if getattr(method_body, 'api', False):
|
|
||||||
for api_version, api_name in getattr(method_body, 'versions', []):
|
|
||||||
if api_version_last < api_version <= version:
|
|
||||||
api_version_last = api_version
|
|
||||||
api_name_last = api_name
|
|
||||||
|
|
||||||
if api_name_last is None and api_version_last == 0:
|
|
||||||
api_name_last = method_name
|
|
||||||
|
|
||||||
if api_name_last is not None and api_name_last == name:
|
|
||||||
method_body(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
|
||||||
def multi(self, actions):
|
def multi(self, actions):
|
||||||
return self.anki.multi(actions)
|
return self.anki.multi(actions)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def storeMediaFile(self, filename, data):
|
def storeMediaFile(self, filename, data):
|
||||||
return self.anki.storeMediaFile(filename, data)
|
return self.anki.storeMediaFile(filename, data)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def retrieveMediaFile(self, filename):
|
def retrieveMediaFile(self, filename):
|
||||||
return self.anki.retrieveMediaFile(filename)
|
return self.anki.retrieveMediaFile(filename)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def deleteMediaFile(self, filename):
|
def deleteMediaFile(self, filename):
|
||||||
return self.anki.deleteMediaFile(filename)
|
return self.anki.deleteMediaFile(filename)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def deckNames(self):
|
def deckNames(self):
|
||||||
return self.anki.deckNames()
|
return self.anki.deckNames()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def deckNamesAndIds(self):
|
def deckNamesAndIds(self):
|
||||||
return self.anki.deckNamesAndIds()
|
return self.anki.deckNamesAndIds()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def modelNames(self):
|
def modelNames(self):
|
||||||
return self.anki.modelNames()
|
return self.anki.modelNames()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def modelNamesAndIds(self):
|
def modelNamesAndIds(self):
|
||||||
return self.anki.modelNamesAndIds()
|
return self.anki.modelNamesAndIds()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def modelFieldNames(self, modelName):
|
def modelFieldNames(self, modelName):
|
||||||
return self.anki.modelFieldNames(modelName)
|
return self.anki.modelFieldNames(modelName)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def modelFieldsOnTemplates(self, modelName):
|
def modelFieldsOnTemplates(self, modelName):
|
||||||
return self.anki.modelFieldsOnTemplates(modelName)
|
return self.anki.modelFieldsOnTemplates(modelName)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def getDeckConfig(self, deck):
|
def getDeckConfig(self, deck):
|
||||||
return self.anki.getDeckConfig(deck)
|
return self.anki.getDeckConfig(deck)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def saveDeckConfig(self, config):
|
def saveDeckConfig(self, config):
|
||||||
return self.anki.saveDeckConfig(config)
|
return self.anki.saveDeckConfig(config)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def setDeckConfigId(self, decks, configId):
|
def setDeckConfigId(self, decks, configId):
|
||||||
return self.anki.setDeckConfigId(decks, configId)
|
return self.anki.setDeckConfigId(decks, configId)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def cloneDeckConfigId(self, name, cloneFrom=1):
|
def cloneDeckConfigId(self, name, cloneFrom=1):
|
||||||
return self.anki.cloneDeckConfigId(name, cloneFrom)
|
return self.anki.cloneDeckConfigId(name, cloneFrom)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def removeDeckConfigId(self, configId):
|
def removeDeckConfigId(self, configId):
|
||||||
return self.anki.removeDeckConfigId(configId)
|
return self.anki.removeDeckConfigId(configId)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def addNote(self, note):
|
def addNote(self, note):
|
||||||
params = AnkiNoteParams(note)
|
params = AnkiNoteParams(note)
|
||||||
if params.validate():
|
if params.validate():
|
||||||
return self.anki.addNote(params)
|
return self.anki.addNote(params)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def addNotes(self, notes):
|
def addNotes(self, notes):
|
||||||
results = []
|
results = []
|
||||||
for note in notes:
|
for note in notes:
|
||||||
@ -1032,7 +1023,7 @@ class AnkiConnect:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def canAddNotes(self, notes):
|
def canAddNotes(self, notes):
|
||||||
results = []
|
results = []
|
||||||
for note in notes:
|
for note in notes:
|
||||||
@ -1042,42 +1033,42 @@ class AnkiConnect:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def addTags(self, notes, tags, add=True):
|
def addTags(self, notes, tags, add=True):
|
||||||
return self.anki.addTags(notes, tags, add)
|
return self.anki.addTags(notes, tags, add)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def removeTags(self, notes, tags):
|
def removeTags(self, notes, tags):
|
||||||
return self.anki.addTags(notes, tags, False)
|
return self.anki.addTags(notes, tags, False)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def suspend(self, cards, suspend=True):
|
def suspend(self, cards, suspend=True):
|
||||||
return self.anki.suspend(cards, suspend)
|
return self.anki.suspend(cards, suspend)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def unsuspend(self, cards):
|
def unsuspend(self, cards):
|
||||||
return self.anki.suspend(cards, False)
|
return self.anki.suspend(cards, False)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def areSuspended(self, cards):
|
def areSuspended(self, cards):
|
||||||
return self.anki.areSuspended(cards)
|
return self.anki.areSuspended(cards)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def areDue(self, cards):
|
def areDue(self, cards):
|
||||||
return self.anki.areDue(cards)
|
return self.anki.areDue(cards)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def getIntervals(self, cards, complete=False):
|
def getIntervals(self, cards, complete=False):
|
||||||
return self.anki.getIntervals(cards, complete)
|
return self.anki.getIntervals(cards, complete)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
response = QMessageBox.question(
|
response = QMessageBox.question(
|
||||||
self.anki.window(),
|
self.anki.window(),
|
||||||
@ -1100,91 +1091,92 @@ class AnkiConnect:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def version(self):
|
def version(self):
|
||||||
return API_VERSION
|
return API_VERSION
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def findNotes(self, query=None):
|
def findNotes(self, query=None):
|
||||||
return self.anki.findNotes(query)
|
return self.anki.findNotes(query)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def findCards(self, query=None):
|
def findCards(self, query=None):
|
||||||
return self.anki.findCards(query)
|
return self.anki.findCards(query)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def getDecks(self, cards):
|
def getDecks(self, cards):
|
||||||
return self.anki.getDecks(cards)
|
return self.anki.getDecks(cards)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def changeDeck(self, cards, deck):
|
def changeDeck(self, cards, deck):
|
||||||
return self.anki.changeDeck(cards, deck)
|
return self.anki.changeDeck(cards, deck)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def deleteDecks(self, decks, cardsToo=False):
|
def deleteDecks(self, decks, cardsToo=False):
|
||||||
return self.anki.deleteDecks(decks, cardsToo)
|
return self.anki.deleteDecks(decks, cardsToo)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def cardsToNotes(self, cards):
|
def cardsToNotes(self, cards):
|
||||||
return self.anki.cardsToNotes(cards)
|
return self.anki.cardsToNotes(cards)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiBrowse(self, query=None):
|
def guiBrowse(self, query=None):
|
||||||
return self.anki.guiBrowse(query)
|
return self.anki.guiBrowse(query)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiAddCards(self):
|
def guiAddCards(self):
|
||||||
return self.anki.guiAddCards()
|
return self.anki.guiAddCards()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiCurrentCard(self):
|
def guiCurrentCard(self):
|
||||||
return self.anki.guiCurrentCard()
|
return self.anki.guiCurrentCard()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiStartCardTimer(self):
|
def guiStartCardTimer(self):
|
||||||
return self.anki.guiStartCardTimer()
|
return self.anki.guiStartCardTimer()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiAnswerCard(self, ease):
|
def guiAnswerCard(self, ease):
|
||||||
return self.anki.guiAnswerCard(ease)
|
return self.anki.guiAnswerCard(ease)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiShowQuestion(self):
|
def guiShowQuestion(self):
|
||||||
return self.anki.guiShowQuestion()
|
return self.anki.guiShowQuestion()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiShowAnswer(self):
|
def guiShowAnswer(self):
|
||||||
return self.anki.guiShowAnswer()
|
return self.anki.guiShowAnswer()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiDeckOverview(self, name):
|
def guiDeckOverview(self, name):
|
||||||
return self.anki.guiDeckOverview(name)
|
return self.anki.guiDeckOverview(name)
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiDeckBrowser(self):
|
def guiDeckBrowser(self):
|
||||||
return self.anki.guiDeckBrowser()
|
return self.anki.guiDeckBrowser()
|
||||||
|
|
||||||
|
|
||||||
@webApi
|
@webApi()
|
||||||
def guiDeckReview(self, name):
|
def guiDeckReview(self, name):
|
||||||
return self.anki.guiDeckReview(name)
|
return self.anki.guiDeckReview(name)
|
||||||
|
|
||||||
@webApi
|
|
||||||
|
@webApi()
|
||||||
def guiExitAnki(self):
|
def guiExitAnki(self):
|
||||||
return self.anki.guiExitAnki()
|
return self.anki.guiExitAnki()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user