some additional actions (#185)
* implement needed actions * add documentation and tests for cardInfo, updateCompleteDeck, reloadCollection, cardReviews, getLatestReviewID, insertReviews Co-authored-by: Julian Müller <julian.mueller@stud-mail.uni-wuerzburg.de>
This commit is contained in:
parent
84586cb352
commit
ce0cc7dce3
@ -271,7 +271,14 @@
|
|||||||
"css":"p {font-family:Arial;}",
|
"css":"p {font-family:Arial;}",
|
||||||
"cardId": 1498938915662,
|
"cardId": 1498938915662,
|
||||||
"interval": 16,
|
"interval": 16,
|
||||||
"note":1502298033753
|
"note":1502298033753,
|
||||||
|
"ord": 1,
|
||||||
|
"type": 0,
|
||||||
|
"queue": 0,
|
||||||
|
"due": 1,
|
||||||
|
"reps": 1,
|
||||||
|
"lapses": 0,
|
||||||
|
"left": 6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"answer": "back content",
|
"answer": "back content",
|
||||||
@ -286,7 +293,14 @@
|
|||||||
"css":"p {font-family:Arial;}",
|
"css":"p {font-family:Arial;}",
|
||||||
"cardId": 1502098034048,
|
"cardId": 1502098034048,
|
||||||
"interval": 23,
|
"interval": 23,
|
||||||
"note":1502298033753
|
"note":1502298033753,
|
||||||
|
"ord": 1,
|
||||||
|
"type": 0,
|
||||||
|
"queue": 0,
|
||||||
|
"due": 1,
|
||||||
|
"reps": 1,
|
||||||
|
"lapses": 0,
|
||||||
|
"left": 6
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"error": null
|
"error": null
|
||||||
|
@ -329,3 +329,73 @@
|
|||||||
"error": null
|
"error": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **updateCompleteDeck**
|
||||||
|
|
||||||
|
Pastes all transmitted data into the database and reloads the collection.
|
||||||
|
You can send a deckName and corresponding cards, notes & models.
|
||||||
|
All cards are assumed to belong to the given deck.
|
||||||
|
All notes referenced by given cards should be present.
|
||||||
|
All models referenced by given notes should be present.
|
||||||
|
|
||||||
|
*Sample request*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "updateCompleteDeck",
|
||||||
|
"version": 6,
|
||||||
|
"params": {
|
||||||
|
"data": {
|
||||||
|
"deck": "test3",
|
||||||
|
"cards": {
|
||||||
|
"1485369472028": {
|
||||||
|
"id": 1485369472028,
|
||||||
|
"nid": 1485369340204,
|
||||||
|
"ord": 0,
|
||||||
|
"type": 0,
|
||||||
|
"queue": 0,
|
||||||
|
"due": 1186031,
|
||||||
|
"factor": 0,
|
||||||
|
"ivl": 0,
|
||||||
|
"reps": 0,
|
||||||
|
"lapses": 0,
|
||||||
|
"left": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"1485369340204": {
|
||||||
|
"id": 1485369340204,
|
||||||
|
"mid": 1375786181313,
|
||||||
|
"fields": [
|
||||||
|
"frontValue",
|
||||||
|
"backValue"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"aTag"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"1375786181313": {
|
||||||
|
"id": 1375786181313,
|
||||||
|
"name": "anotherModel",
|
||||||
|
"fields": [
|
||||||
|
"Front",
|
||||||
|
"Back"
|
||||||
|
],
|
||||||
|
"templateNames": [
|
||||||
|
"Card 1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Sample result*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": null,
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
@ -168,3 +168,23 @@
|
|||||||
"error": null
|
"error": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **reloadCollection**
|
||||||
|
|
||||||
|
Tells anki to reload all data from the database.
|
||||||
|
|
||||||
|
*Sample request*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "reloadCollection",
|
||||||
|
"version": 6
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Sample result*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": null,
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
@ -42,3 +42,81 @@
|
|||||||
"result": "<center> lots of HTML here </center>"
|
"result": "<center> lots of HTML here </center>"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **cardReviews**
|
||||||
|
|
||||||
|
Requests all card reviews for a specified deck after a certain time.
|
||||||
|
`startID` is the latest unix time not included in the result.
|
||||||
|
Returns a list of 9-tuples `(reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)`
|
||||||
|
|
||||||
|
*Sample request*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "cardReviews",
|
||||||
|
"version": 6,
|
||||||
|
"params": {
|
||||||
|
"deck": "default",
|
||||||
|
"startID": 1594194095740
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Sample result*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": [
|
||||||
|
[1594194095746, 1485369733217, -1, 3, 4, -60, 2500, 6157, 0],
|
||||||
|
[1594201393292, 1485369902086, -1, 1, -60, -60, 0, 4846, 0]
|
||||||
|
],
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* **getLatestReviewID**
|
||||||
|
|
||||||
|
Returns the unix time of the latest review for the given deck. 0 if no review has ever been made for the deck.
|
||||||
|
|
||||||
|
*Sample request*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "getLatestReviewID",
|
||||||
|
"version": 6,
|
||||||
|
"params": {
|
||||||
|
"deck": "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Sample result*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": 1594194095746,
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* **insertReviews**
|
||||||
|
|
||||||
|
Inserts the given reviews into the database. Required format: list of 9-tuples `(reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)`
|
||||||
|
|
||||||
|
*Sample request*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "insertReviews",
|
||||||
|
"version": 6,
|
||||||
|
"params": {
|
||||||
|
"reviews": [
|
||||||
|
[1594194095746, 1485369733217, -1, 3, 4, -60, 2500, 6157, 0],
|
||||||
|
[1594201393292, 1485369902086, -1, 1, -60, -60, 0, 4846, 0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Sample result*:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": null,
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -34,6 +34,7 @@ import anki.storage
|
|||||||
import aqt
|
import aqt
|
||||||
from anki.exporting import AnkiPackageExporter
|
from anki.exporting import AnkiPackageExporter
|
||||||
from anki.importing import AnkiPackageImporter
|
from anki.importing import AnkiPackageImporter
|
||||||
|
from anki.utils import joinFields, intTime, guid64, fieldChecksum
|
||||||
|
|
||||||
from . import web, util
|
from . import web, util
|
||||||
|
|
||||||
@ -877,13 +878,20 @@ class AnkiConnect:
|
|||||||
'question': util.getQuestion(card),
|
'question': util.getQuestion(card),
|
||||||
'answer': util.getAnswer(card),
|
'answer': util.getAnswer(card),
|
||||||
'modelName': model['name'],
|
'modelName': model['name'],
|
||||||
|
'ord': card.ord,
|
||||||
'deckName': self.deckNameFromId(card.did),
|
'deckName': self.deckNameFromId(card.did),
|
||||||
'css': model['css'],
|
'css': model['css'],
|
||||||
'factor': card.factor,
|
'factor': card.factor,
|
||||||
#This factor is 10 times the ease percentage,
|
#This factor is 10 times the ease percentage,
|
||||||
# so an ease of 310% would be reported as 3100
|
# so an ease of 310% would be reported as 3100
|
||||||
'interval': card.ivl,
|
'interval': card.ivl,
|
||||||
'note': card.nid
|
'note': card.nid,
|
||||||
|
'type': card.type,
|
||||||
|
'queue': card.queue,
|
||||||
|
'due': card.due,
|
||||||
|
'reps': card.reps,
|
||||||
|
'lapses': card.lapses,
|
||||||
|
'left': card.left,
|
||||||
})
|
})
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
# Anki will give a TypeError if the card ID does not exist.
|
# Anki will give a TypeError if the card ID does not exist.
|
||||||
@ -895,6 +903,68 @@ class AnkiConnect:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@util.api()
|
||||||
|
def cardReviews(self, deck, startID):
|
||||||
|
return self.database().all("select id, cid, usn, ease, ivl, lastIvl, factor, time, type from revlog "
|
||||||
|
"where id>? and cid in (select id from cards where did=?)",
|
||||||
|
startID, self.decks().id(deck))
|
||||||
|
|
||||||
|
@util.api()
|
||||||
|
def reloadCollection(self):
|
||||||
|
self.collection().reset()
|
||||||
|
|
||||||
|
@util.api()
|
||||||
|
def getLatestReviewID(self, deck):
|
||||||
|
return self.database().scalar("select max(id) from revlog where cid in (select id from cards where did=?)",
|
||||||
|
self.decks().id(deck)) or 0
|
||||||
|
|
||||||
|
@util.api()
|
||||||
|
def updateCompleteDeck(self, data):
|
||||||
|
self.startEditing()
|
||||||
|
did = self.decks().id(data["deck"])
|
||||||
|
self.decks().flush()
|
||||||
|
model_manager = self.collection().models
|
||||||
|
for _, card in data["cards"].items():
|
||||||
|
self.database().execute(
|
||||||
|
"replace into cards (id, nid, did, ord, type, queue, due, ivl, factor, reps, lapses, left, "
|
||||||
|
"mod, usn, odue, odid, flags, data) "
|
||||||
|
"values (" + "?," * (12 + 6 - 1) + "?)",
|
||||||
|
card["id"], card["nid"], did, card["ord"], card["type"], card["queue"], card["due"],
|
||||||
|
card["ivl"], card["factor"], card["reps"], card["lapses"], card["left"],
|
||||||
|
intTime(), -1, 0, 0, 0, 0
|
||||||
|
)
|
||||||
|
note = data["notes"][str(card["nid"])]
|
||||||
|
tags = self.collection().tags.join(self.collection().tags.canonify(note["tags"]))
|
||||||
|
self.database().execute(
|
||||||
|
"replace into notes(id, mid, tags, flds,"
|
||||||
|
"guid, mod, usn, flags, data, sfld, csum) values (" + "?," * (4 + 7 - 1) + "?)",
|
||||||
|
note["id"], note["mid"], tags, joinFields(note["fields"]),
|
||||||
|
guid64(), intTime(), -1, 0, 0, "", fieldChecksum(note["fields"][0])
|
||||||
|
)
|
||||||
|
model = data["models"][str(note["mid"])]
|
||||||
|
if not model_manager.get(model["id"]):
|
||||||
|
model_o = model_manager.new(model["name"])
|
||||||
|
for field_name in model["fields"]:
|
||||||
|
field = model_manager.newField(field_name)
|
||||||
|
model_manager.addField(model_o, field)
|
||||||
|
for template_name in model["templateNames"]:
|
||||||
|
template = model_manager.newTemplate(template_name)
|
||||||
|
model_manager.addTemplate(model_o, template)
|
||||||
|
model_o["id"] = model["id"]
|
||||||
|
model_manager.update(model_o)
|
||||||
|
model_manager.flush()
|
||||||
|
|
||||||
|
self.stopEditing()
|
||||||
|
|
||||||
|
@util.api()
|
||||||
|
def insertReviews(self, reviews):
|
||||||
|
if len(reviews) == 0: return
|
||||||
|
sql = "insert into revlog(id,cid,usn,ease,ivl,lastIvl,factor,time,type) values "
|
||||||
|
for row in reviews:
|
||||||
|
sql += "(%s)," % ",".join(map(str, row))
|
||||||
|
sql = sql[:-1]
|
||||||
|
self.database().execute(sql)
|
||||||
|
|
||||||
@util.api()
|
@util.api()
|
||||||
def notesInfo(self, notes):
|
def notesInfo(self, notes):
|
||||||
result = []
|
result = []
|
||||||
|
@ -67,5 +67,32 @@ class TestDecks(unittest.TestCase):
|
|||||||
deckConfigId = util.invoke('cloneDeckConfigId', cloneFrom=deckConfigId, name='test')
|
deckConfigId = util.invoke('cloneDeckConfigId', cloneFrom=deckConfigId, name='test')
|
||||||
self.assertFalse(deckConfigId)
|
self.assertFalse(deckConfigId)
|
||||||
|
|
||||||
|
# updateCompleteDeck
|
||||||
|
util.invoke("updateCompleteDeck", data={
|
||||||
|
"deck": "test3",
|
||||||
|
"cards": {
|
||||||
|
"12": {
|
||||||
|
"id": 12, "nid": 23, "ord": 0, "type": 0, "queue": 0,
|
||||||
|
"due": 1186031, "factor": 0, "ivl": 0, "reps": 0, "lapses": 0, "left": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"23": {
|
||||||
|
"id": 23, "mid": 34, "fields": ["frontValue", "backValue"], "tags": ["aTag"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"34": {
|
||||||
|
"id": 34, "fields": ["Front", "Back"], "templateNames": ["Card 1"], "name": "anotherModel",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
deckNames = util.invoke("deckNames")
|
||||||
|
self.assertIn("test3", deckNames)
|
||||||
|
cardIDs = util.invoke('findCards', query='deck:test3')
|
||||||
|
self.assertEqual(len(cardIDs), 1)
|
||||||
|
self.assertEqual(cardIDs[0], 12)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -52,6 +52,9 @@ class TestMisc(unittest.TestCase):
|
|||||||
deckNames = util.invoke('deckNames')
|
deckNames = util.invoke('deckNames')
|
||||||
self.assertIn(deckName, deckNames)
|
self.assertIn(deckName, deckNames)
|
||||||
|
|
||||||
|
# reloadCollection
|
||||||
|
util.invoke("reloadCollection")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -6,7 +6,15 @@ import unittest
|
|||||||
import util
|
import util
|
||||||
|
|
||||||
|
|
||||||
class TestMisc(unittest.TestCase):
|
class TestStats(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
util.invoke('createDeck', deck='test')
|
||||||
|
note = {'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front1', 'Back': 'back1'}, 'tags': ['tag1']}
|
||||||
|
self.noteId = util.invoke('addNote', note=note)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
util.invoke('deleteDecks', decks=['test'], cardsToo=True)
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
# getNumCardsReviewedToday
|
# getNumCardsReviewedToday
|
||||||
result = util.invoke('getNumCardsReviewedToday')
|
result = util.invoke('getNumCardsReviewedToday')
|
||||||
@ -16,6 +24,20 @@ class TestMisc(unittest.TestCase):
|
|||||||
result = util.invoke('getCollectionStatsHTML')
|
result = util.invoke('getCollectionStatsHTML')
|
||||||
self.assertIsInstance(result, str)
|
self.assertIsInstance(result, str)
|
||||||
|
|
||||||
|
# no reviews for new deck
|
||||||
|
self.assertEqual(len(util.invoke("cardReviews", deck="test", startID=0)), 0)
|
||||||
|
self.assertEqual(util.invoke("getLatestReviewID", deck="test"), 0)
|
||||||
|
|
||||||
|
# add reviews
|
||||||
|
cardId = int(util.invoke('findCards', query='deck:test')[0])
|
||||||
|
latestID = 123456 # small enough to not interfere with existing reviews
|
||||||
|
util.invoke("insertReviews", reviews=[
|
||||||
|
[latestID-1, cardId, -1, 3, 4, -60, 2500, 6157, 0],
|
||||||
|
[latestID, cardId, -1, 1, -60, -60, 0, 4846, 0]
|
||||||
|
])
|
||||||
|
self.assertEqual(len(util.invoke("cardReviews", deck="test", startID=0)), 2)
|
||||||
|
self.assertEqual(util.invoke("getLatestReviewID", deck="test"), latestID)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user